Use Delphi Dll and some problems - c#

I want to use a dll that made by Delphi. It has this function :
function CryptStr(str, Key : AnsiString; DecryptStr : boolean) : AnsiString; stdcall;
I copied the Dll in /bin/debug and in application root. my code is :
[DllImport("Crypt2.dll", EntryPoint = "CryptStr", CallingConvention = CallingConvention.StdCall)]
static extern string CryptStr( string str, string Key, bool DecryptStr);
public string g = "";
private void Form1_Load(object sender, EventArgs e)
{
g=CryptStr("999", "999999", true);
MessageBox.Show(g);
}
I have some problem :
1. even I delete Dll from those path application doesn't throw not found exception
2. when application run in g=CryptStr("999", "999999", true); it finishes execution and show the form without running Messagebox line.
I tried to use Marshal but above errors remain.

You cannot expect to call that function from a programming environment other than Delphi. That's because it uses Delphi native strings which are not valid for interop. Even if you call from Delphi you need to use the same version of Delphi as was used to compile the DLL, and the ShareMem unit so that the memory manager can be shared. That function is not even well designed for interop between two Delphi modules.
You need to change the DLL function's signature. For example you could use:
procedure CryptStr(
str: PAnsiChar;
Key: PAnsiChar;
DecryptStr: boolean;
output: PAnsiChar;
); stdcall;
In C# you would declare this like so:
[DllImport("Crypt2.dll")]
static extern void CryptStr(
string str,
string Key,
bool DecryptStr,
StringBuilder output
);
This change requires the caller to allocate the buffer that is passed to the function. If you want to find examples of doing that, search for examples calling the Win32 API GetWindowText.
If you were using UTF-16 text instead of 8 bit ANSI, you could use COM BSTR which is allocated on the shared COM heap, but I suspect that option is not available to you.
As for your program not showing any errors, I refer you to these posts:
http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
http://blog.adamjcooper.com/2011/05/why-is-my-exception-being-swallowed-in.html
Silent failures in C#, seemingly unhandled exceptions that does not crash the program

Related

UnmanagedExports Function Parameters - Cause VBA 49 Bad DLL Calling Convention

I am unable to get my C# Dll to work with my Excel Macro enable spreadsheet to call the function in the Dll. I am able to call the function if it has no parameters, able to return the expected value. But I cannot get the Dll call from VBA to be successful when I add input parameters to the function.
The sum total of my C# module is as follows:
using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace RSExternalInterface
{
public class RSExternalInterface
{
[DllExport("add", CallingConvention = CallingConvention.Cdecl)]
public static int TestExport(int left, int right)
{
return left + right;
}
}
}
My VBA Code is as follows.
Module Declaration:
Declare Function add Lib "C:\Program Files\RS\RSExternalInterface.dll" (ByVal Left As Integer, ByVal Right as Integer) As Integer
Call the function above, inside the click event sub of a button, is as follows:
result = add (5, 7)
The moment the above line is executed, Error 49 comes up.
I have tried the following:
Removed the "ByVal" in the VBA function declaration
Removed and added the decoration in the C# function, after [DllExports ...]
Ensured that I target the correct CPU platform
Removed the parameters in the declaration and calling to ensure that the DLL function is accessible.
What am I doing wrong?
I am surprised that "Calling Convention" from question title and CallingConvention from C# code didn't ring any bell (although I don't rule out a misleading error message on MS side).
Calling convention is like a protocol when executing subroutines, between:
callee - the routine (function, procedure (or function that doesn't return anything, or returns void))
caller - code (a subroutine as well - could be main) that calls/executes the callee
and determines:
Who handles (push / pop) the function arguments on the stack (callee / caller) when executing it
Arguments order on the stack (right -> left or left -> right)
Because of #1., it's important that the 2 parties (callee / caller) to be in synch regarding calling convention, otherwise they will setp on each other's toes, and the stack will eventually get corrupted. "Bad DLL Calling Convention" means exactly this thing: the 2 are not in synch.
You get correct behavior for function without arguments, because there's nothing to push / pop to/from the stack - this doesn't mean that there isn't any error; the error is there but it's not encountered
More details: [MSDN]: Argument Passing and Naming Conventions. Note differences between __stdcall and __cdecl, and also note that they apply to 32bit (x86) architecture. AFAIK (also backed up by the error), Excel executable (as whole MSOffice) is 32bit
According to [MSDN]: DllImportAttribute.CallingConvention Field (which I assume also applies to DllExport):
You set this field to one of the CallingConvention enumeration members. The default value for the CallingConvention field is Winapi, which in turn defaults to StdCall convention.
I took your code, and played a little bit with it.
code.cs:
using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace RSExternalInterface
{
public class RSExternalInterface
{
[DllExport("add")]
public static int TestExport(int left, int right)
{
int ret = left + right;
String s = String.Format("C# Func - add: {0} + {1} = {2}", left, right, ret);
Console.WriteLine(s);
return ret;
}
[DllExport("dbl", CallingConvention = CallingConvention.Winapi)]
public static int TestExport1(int value)
{
int ret = value * 2;
String s = String.Format("C# Func - dbl: {0} * 2 = {1}", value, ret);
Console.WriteLine(s);
return ret;
}
[DllExport("none", CallingConvention = CallingConvention.StdCall)]
public static void TestExport2(int value)
{
String s = String.Format("C# Func - none: {0}", value);
Console.WriteLine(s);
}
}
}
main.vb:
Module main
Declare Function add Lib "CsDll.dll" (ByVal Left As Integer, ByVal Right As Integer) As Integer
Declare Function dbl Lib "CsDll.dll" (ByVal Value As Integer) As Integer
Declare Sub none Lib "CsDll.dll" (ByVal Value As Integer)
Sub Main()
Console.WriteLine("64 bit OS: {0}{1}64 bit process: {2}{1}", Environment.Is64BitOperatingSystem, Environment.NewLine, Environment.Is64BitProcess)
Dim i As Integer
none(-7659)
i = dbl(78)
i = add(22, -13)
End Sub
End Module
Notes:
I wasn't able to build it and run it in Excel, so I tried the 2nd best thing: do it from VB. Note that I'm experienced in neither C# nor VB
At the beginning (as I stated in a comment), I wasn't able to export anything from the C# .dll. Looking at UnmanagedExports.nuspec (part of the nupkg):
You have to set your platform target to either x86, ia64 or x64. AnyCPU assemblies cannot export functions.
Changed the Platform from Any CPU (VStudio default) to x64 (by mistake), and everything ran fine (in spite of CallingConvention.Cdecl), only after setting it to x86 I was able to reproduce the problem
Anyway, the (C#) code contains 3 ways of (not) specifying a calling convention, that will work from VB
Output (VStudio 2015 (Community)):
64 bit OS: True
64 bit process: False
C# Func - none: -7659
C# Func - dbl: 78 * 2 = 156
C# Func - add: 22 + -13 = 9
Here's an (ancient) URL that also mentions your error: [MSDN]: Bad DLL calling convention (Error 49)

Java JNI call to C# COM fails, when COM is registered without codebase option of regasm

Function calls from Java to C# through JNI-C++/CLI are failing when the C# COM is not registered using regasm with the codebase option. I've built a sample following the instructions in P2: Calling C# from Java with some changes.
Numero uno: C#
Change the C# dll into a COM by creating an interface, IRunner, and making the library assembly COM-visible.
namespace RunnerCOM
{
public interface IRunner
{
String ping();
}
public class Runner:IRunner
{
static void Main(string[] args)
{
}
public Runner() { }
public String ping()
{
return "Alive (C#)";
}
}
}
Numero due: Java
No changes made to the Java section.
Numero tre: C++
This part was changed to create a new instance of the RunnerCOM.Runner class and use that result. Here is a good tutorial on how to call managed code from unmanaged code: http://support.microsoft.com/kb/828736
#include "stdafx.h"
#include "Runner.h"
#pragma once
#using <mscorlib.dll>
#import "RunnerCOM.tlb"
JNIEXPORT jstring JNICALL Java_Runner_ping(JNIEnv *env, jobject obj){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, true);
char cap[128];
for(unsigned int i=0;i<temp.length();i++){
cap[i] = (char)ping[i];
}
return env->NewStringUTF(cap);
}
Now to my questions,
The code above fails with a _com_error exception, Class not registered (0x80040154) unless the codebase option is enabled during regsitration of RunnerCOM.dll, with regasm.exe. Why is this? If the code is not ran from JNI, I tested it as an exe, it works fine. The RunnerCOM.dll is simply found in the working directory.
Type casting _bstr_t temp to char* fails. For example, char *out = (char*) temp; Similar to the issue above, it works fine when it's built and executed as an exe but crashes the JVM when it's a JNI call.
By the way this is what I used to run it as an executable:
int main(){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, false);
printf(temp);
return 0;
}
Codebase creates a Codebase entry in the registry. The Codebase entry specifies the file path for an assembly that is not installed in the global assembly cache, so when you specify the codebase, the system will find the DLL based on the path. If not, it will try to locate the dll in the GAC and current working directory. In JNI, I think the current working directory is not the folder where the DLL is. You can use process explorer to find what is the current working directory, also, you can use process monitor to find out which directories the exe is looking into to find the dll.
The code converting _bstr_t to char*, the char* string cap is not ended with '\0', I think this might cause problem in JNI. Uses the _bstr_t operator (char *), you can obtain a null terminated string from the _bstr_t object. Please check the msdn example for details.
You mentioned C++/CLI, C++/Cli and COM warpper are two different ways to interop with C# code. If you're using C++/CLI as a bridge, you doesn't need to register C# DLL as COM, please see this: Calling .Net Dlls from Java code without using regasm.
If you're using COM, you should call CoInitialize() to init COM first in your code.

C# Interop Delphi DLL

I Have a third party DLL write in Delphi "a.dll" (without source).
And this DLL has one method with this signature.
function GetAny(pFileName: String): String;
I can't do a interop call from c# because 'String type' has private access in delphi.
So a build another DLL in delphi to wrapper that call.
Delphi.
function GetAny(pFileName: String): String; external 'a.dll'
function GetWrapper(url : PChar) : PChar; stdcall;
begin
Result := PChar(GetAny(url)); // I need avoid this String allocation, is throwing a exception.
end;
C#.
[DllImport("wrapper.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr GetWrapper(String url);
Inside "GetWrapper" i make a call to external "GetAny", the result is OK (in delphi i can debug), but before i get this result back in c# side, it's throwing a Exception.
IntPtr test = GetWrapper("a");
String result = Marshal.PtrToStringAnsi(test);
Your wrapper DLL also cannot call GetAny because string is a managed Delphi type that cannot be passed across module boundaries.
The problem is that the return value of GetAny is allocated in one module and deallocated in a different module. It is allocated in the DLL that implements GetAny, and deallocated in the DLL that calls GetAny. Since the two DLLs use different memory managers, you end up trying to deallocate memory that was allocated on a different heap.
If the DLL that implements GetAny can be persuaded to share a memory manager then you could solve that problem readily.
I do question the facts that you present though. As it stands, unless the DLL is designed to be used with ShareMem, that function can never be called safely.
If you were prepared to leak the memory you could try this:
Delphi
function GetAny(pFileName: string): PChar; external 'a.dll'
procedure GetWrapper(url: PChar; out value: WideString); stdcall;
var
P: PChar;
begin
P := GetAny(url);
if Assigned(P) then
Value := P
else
Value := '';
end;
C#
[DllImport("wrapper.dll"]
public static extern void GetWrapper(
string url,
[MarshalAs(UnmanagedType.BStr)]
out string value
);
I've downloaded your code...
The solution could be like this:
Create a wrapper procedure in Delphi with "cdecl" declaration with 2 parameters of PChar type
the first one is IN parameter
the second one is OUT parameter
Original Delphi function:
function GetAny(pFileName: String): String; external 'a.dll';
Delphi – DLL with wrapped function:
procedure GetWrapper (url: PChar; var urlNew: PChar) cdecl;
var str: string;
begin
urlStr = string(url);
urNewStr := GetAny(urlStr);
urlNew := PChar(urNewStr);
end;
exports
GetWrapper;
begin
end.
In Visual Studio:
Make the project x32 bit (not a x64 as it was in your sample)
Import DLL as Cdecl
[DllImport("wrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
Marshaling
public static extern void GetWrapper ([MarshalAs(UnmanagedType.LPStr)]string url, [MarshalAs(UnmanagedType.LPStr)] out string urlNew);
Calling in C#:
string fileName;// = #"wertwertwertwertwer";
GetWrapper("2.jpg", out fileName);
Console.WriteLine(fileName);
In my environment it worked. (Delphi 5 and VS2012).
Hopefully it will work for you also.

Access Violation Exception

I am having some strange problem. I have written a small module in VC++ using OpenCV.
It works fine. The code aggregates feed from the CCTV camera connected to the USB port.
however, I had to write the rest of my application in C#, so I made a DLL of the VC++ code and called the VC++ method from C#.
Now, I have ended up getting an error
Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
Can anyone please suggest me any solution to this. Is there any Access Violation while accessing it in a managed code?
If TrackBlob returns a string, you should be able to define your dllimport as such:
[DllImport("Tracking.dll", EntryPoint = "TrackIt")]
public extern static string TrackBlob();
and skip trying to marshal it.
By returning it as an IntPtr, you're trying to get a pointer into memory owned by the unmanaged DLL... returning it as a string will return a copy of the string for you to work with.
Let me know if that works!
James
* Edit *
Try one of these:
[DllImport("Tracking.dll", EntryPoint = "TrackIt")]
public extern static [MarshalAs(UnmanagedType.BStr)] string TrackBlob();
or
[DllImport("Tracking.dll", EntryPoint = "TrackIt")]
public extern static [MarshalAs(UnmanagedType.AnsiBStr)] string TrackBlob();
Check out this MSDN link on string marshalling:
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

"Unable to find an entry point named [function] in dll" (c++ to c# type conversion)

I have a dll which comes from a third party, which was written in C++.
Here is some information that comes from the dll documentation:
//start documentation
RECO_DATA{
wchar_t Surname[200];
wchar_t Firstname[200];
}
Description:
Data structure for receiving the function result. All function result will be
stored as Unicode (UTF-8).
Method:
bool recoCHN_P_Name(char *imgPath,RECO_DATA *o_data);
Input:
char * imgPath
the full path of the image location for this
function to recognize
RECO_DATA * o_data
data object for receiving the function
result.
Function return:
True if Success, otherwise false will return.
//end documentation
I am trying to call the recoCHN_P_Name from my C# application. To this end, I came up with this code:
The code to import the dll:
public class cnOCRsdk
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECO_DATA{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=200)]
public string FirstName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string Surname;
}
[DllImport(#"cnOCRsdk.dll", EntryPoint="recoCHN_P_Name")]
public static extern bool recoCHN_P_Name(byte[] imgPath, RECO_DATA o_data);
}
The code to call the function:
cnOCRsdk.RECO_DATA recoData = new cnOCRsdk.RECO_DATA();
string path = #"C:\WINDOWS\twain_32\twainrgb.bmp";
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] bytes = encoding.GetBytes(path);
bool res = cnOCRsdk.recoCHN_P_Name(bytes, recoData);
And the error I'm getting is
""Unable to find an entry point named 'recoCHN_P_Name' in DLL 'cnOCRsdk.dll'."
I'm suspecting that I'm having an error in converting a type from C++ to C#. But where exactly ... ?
First make sure the function is actually exported:
In the Visual Studio Command Prompt, use dumpbin /exports whatever.dll
C# doesn't support C++ name mangling and you either need to declare the C++ functions with
extern "C" {...}
(may not an option if they're from a third party), or call the mangled name directly if you can get it to work. It may be easier to get the third party to provide a non-mangled interface to the functionality.
Solved - at least to the point where the program does not break and actually returns me a bool value.
The key, I guess, was to specify the entry point as the 'mangled' name
[DllImport(#"cnOCRsdk.dll", EntryPoint="?recoCHN_P_Name#CcnOCRsdk##QAE_NPADPAURECO_DATA###Z")]
public static extern bool recoCHN_P_Name(ref string imgPath, ref RECO_DATA o_data);
After that I got some other errors but the 'unable to find entry point' went away.
I solved the same problem in these steps:
step 1) If you program your custom DLL in C++ using Visual studio,then at the property page of your project set the Common Language Runtime Support (/clr)parameter to Common Language Runtime Support (/clr).
step 2) To function deceleration in .h file use __declspec(dllexport) keyword like below:
__declspec(dllexport) double Sum(int a,int b);
step 3) Build and export DLL file, then use the Dependency Walker software to get your function EntryPoint.
step4) Import DLL file In the C# project and set EntryPoint and CallingConvention variable like below:
[DllImport("custom.dll", EntryPoint = "?Sum##YAXHHHHHHNNN#Z", CallingConvention = CallingConvention.Cdecl)]
public static extern double Sum(int a,int b);
I'd write a wrapper using C++/CLI. This wrapper will be able to include the .h files and link to the .lib files you got from the third party vendor. Then it is both easy and safe to write a managed interface for your C# program.
Correct EntryPoint string could be found in ".lib" file that comes along with main unmanaged dll.
We had this problem when we want to access to DB and solved it by changing EF core to EF 6.4.4
It may be you have a problem like this and need to change or downgrade your version of EF (If you used EF)
We had this problem .we change EntityFramework.core to EntityFrameWork 6.4.4 and after that the program worked fine. you most change you're Framework Version.
you may get this error due to string marshalling mismatch between DLL and your application . for example, one is using ANSI and the other is unicode.
you can try something like this:
[DllImport("yourDLL.dll", CharSet = CharSet.Unicode )]
public static extern String YourFunction(String name);
checkout HERE for a list of other possible reasons.
You could try using the unmangled name while specifying a CallingConvention in the DllImport

Categories

Resources