I have a below DLL source code.
library Project1;
uses
System.SysUtils,
System.Classes;
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function GetMethodValueAsString():PAnsiChar; stdcall;
end;
TStringFunctions = class(TInterfacedObject, IStringFunctions)
public
function GetMethodValueAsString():PAnsiChar; stdcall;
end;
{$R *.res}
function TStringFunctions.GetMethodValueAsString():PAnsiChar; stdcall;
begin
Result := 'test';
end;
procedure GetImplementation(out instance:IStringFunctions); stdcall; export;
begin
instance := TStringFunctions.Create;
end;
exports GetImplementation;
begin
end.
I want to using in C# like this
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[ComVisible(true)]
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("240B567B-E619-48E4-8CDA-F6A722F44A71")]
public interface IStringFunctions
{
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
[return: MarshalAs(UnmanagedType.AnsiBStr)]
string GetMethodValueAsString();
}
class Program
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.StdCall)]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.StdCall)]
static extern bool FreeLibrary(int hModule);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
delegate void GetImplementation([MarshalAs(UnmanagedType.Interface)] out IStringFunctions instance);
static void Main(string[] args)
{
const string dllName = "Project1.dll";
const string functionName = "GetImplementation";
int libHandle = LoadLibrary(dllName);
if (libHandle == 0) throw new Exception(string.Format("Could not load library \"{0}\"", dllName));
var delphiFunctionAddress = GetProcAddress(libHandle, functionName);
if (delphiFunctionAddress == IntPtr.Zero) throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", functionName, dllName));
GetImplementation getImplementation = (GetImplementation)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(GetImplementation));
if (getImplementation != null)
{
IStringFunctions instance = null;
getImplementation(out instance);
if (instance != null)
{
//!!! don't return value !!!!
String result = instance.GetMethodValueAsString();
Console.WriteLine(result);
}
}
Console.ReadLine();
}
}
}
But instance.GetMethodValueAsString method doesn't working. And exit code.
I want to use returning value from dll function(GetMethodValueAsString) in c#.
I don't understand.
Where's my fault?
Thank you so much
[return: MarshalAs(UnmanagedType.AnsiBStr)]
This is wrong. You are not returning an ANSI encoded string, allocated on the COM heap. You are returning a plain C string, a pointer to null-terminated array of ANSI characters.
Your interface declaration should be:
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
IntPtr GetMethodValueAsString();
Calling the method must be done like this:
IntPtr ptr = instance.GetMethodValueAsString();
string result = Marshal.PtrToStringAnsi(ptr);
Of course, your interface design becomes rather impractical when you need to return a dynamically allocated string. You'd need to export a deallocator too. The clean way to deal with this is to use a BSTR. Like this:
Delphi
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
procedure GetMethodValueAsString(out value: WideString); stdcall;
end;
C#
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
void GetMethodValueAsString([MarshalAs(UnmanagedType.BStr)] out string result);
Is that Delphi DLL visible to your C# code for COM Interop? If not, the easiest way to do this would be to attach the dll to the C# class library project using the "Add Existing Item" menu option. Then in the properties window for this dll set "BuildAction" to None, and "Copy to Output Directory" to "Copy always"
Then you could do something like this in the C# code.
[DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern string GetMethodValueAsString();
Then wherever you need to call that function you could do
var outputMessage = GetMethodValueAsString();
Console.WriteLine(outputMessage);
Related
I am trying to pass a string variable from c# to fortran dll.
My C# code;
namespace fortrandll
{
class Program
{
[DllImport(#"csbdll10.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sub")]
public static extern void sub(ref StringBuilder dbname, ref int n);
static void Main(string[] args)
{
StringBuilder dbname = new StringBuilder(5);
dbname.Append("dbname");
int n = 5;
sub(ref dbname, ref n);
Console.ReadLine();
}
}
}
Fortran code;
subroutine sub(dbname,leng) bind(C,name="sub")
use iso_c_binding
!DEC$ ATTRIBUTES reference :: dbname
!DEC$ ATTRIBUTES VALUE :: len
implicit none
integer, intent(in)::leng
character, intent(in):: dbname(leng)
write(*,*) dbname
return
end
Both the programs are compiled successfully.
But when I run the C# code getting some ascii code as result of fortran call rather intended output.
When I tried without ref keyword in C# getting Access Violation Exception.
Other things tried are Unmanaged datatypes and char bytes.
Thanks for the help.
Update: After removing 'ref' keyword for Stringbuilding and in functional call, getting the expected output.
Update Code:
class Program
{
[DllImport(#"csbdll10.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sub")]
public static extern void sub(StringBuilder dbname, ref int n);
static void Main(string[] args)
{
StringBuilder dbname = new StringBuilder();
dbname.Append("dbname name is here");
int n = dbname.Length;
sub(dbname, ref n);
Console.ReadLine();
}
}
I'm working on building a wrapper for a win32 dll from the USPS. I've gotten the calls to work when the pointers are passed as parameters because I can allocate and free the memory before I send the value to the function. The problem is when the data is passed back in the return value as a char *.
Here is the call in the C dll I'm working with:
const char* z4LLkGetKeySTD(void);
Here is the C# code for the DLLImport call:
[DllImport("C:\\AMS\\zip4_w32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, EntryPoint = "z4SLNKGetVersionSTD",
ExactSpelling = false), ReliabilityContract(Consistency.WillNotCorruptState, Cer.None)]
private static extern IntPtr z4SLNKGetVersionSTD();
The public facing C# method for the above private call:
public static string z4SLNKGetVersion()
{
IntPtr versionPtr = IntPtr.Zero;
string version;
versionPtr = z4SLNKGetVersionSTD();
version = Marshal.PtrToStringAnsi(versionPtr);
versionPtr = IntPtr.Zero;
return version;
}
The code fails sometimes on the call to z4SLNKGetVersionSTD().
I know that returning pointers to strings in C isn't the best idea but I don't have access to the code for the dll.
New Programmer in need of Help!
The Delphi code that is compiled into the DLL
function SetCurrentSerial(Size : Integer; Msg : Pointer) : Integer stdcall;
var
TempByte : PByte;
TempStr : string;
i: Integer;
begin
Result := 0;
TempByte := Msg;
TempStr := '';
for i := 0 to Size - 1 do
begin
TempStr := TempStr + ' ';
end;
for i := 0 to Size - 1 do
begin
TempStr[i+1] := Chr(TempByte^);
Inc(TempByte);
end;
if not DLLClass.SelectDeviceSerial(TempStr) then
begin
Result := -1;
end;
end;
The C# Code
//Import a Function Pointer
[DllImport("Test.dll", CallingConvention= CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public unsafe static extern int SetCurrentSerial(int Size, byte[] Msg);
I need to store the pointer value, Size and Msg in a buffer and print the value in a console window.
I will greatly appreciate a fully constructed code. Thank you in advance.
Here is the code I've so far tried.
//C# Code
class Program
{
[DllImport("Test.dll")]
public unsafe static extern int SetCurrentSerial(int Size, void* Msg);
unsafe static void Main()
{
int Res;
byte[] buffer = new byte[1024];
Res = SetCurrentSerial(255, &buffer);
Console.WriteLine("%s\n", buffer);
}
}
Your DLL function is designed incorrectly. You are passing a string from the calling code to the DLL. That is really simple to do and you can remove almost all of your code. The Delphi code should be like this:
function SetCurrentSerial(Serial: PAnsiChar): LongBool; stdcall;
begin
Result := DLLClass.SelectDeviceSerial(Serial);
end;
Then the C# code should be:
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern bool SetCurrentSerial(string Serial);
Call the function like this:
bool succeeded = SetCurrentSerial(Serial);
if (!succeeded)
{
// handle error
}
I've replaced your integer return value with a boolean indicating success or failure. Should you prefer to revert to an integer that would look like this:
Delphi
function SetCurrentSerial(Serial: PAnsiChar): Integer; stdcall;
begin
if DLLClass.SelectDeviceSerial(Serial) then begin
Result := 0;
end else begin
Result := -1;
end;
end;
C#
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern int SetCurrentSerial(string Serial);
Update
Apparently you cannot change this DLL. That is a shame because it is really very badly designed and implemented. However, to call that function from C# you need to declare the p/invoke like this:
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SetCurrentSerial(int Size, byte[] Msg);
And then call it like this:
byte[] Msg = Encoding.Default.GetBytes(serial);
int retval = SetCurrentSerial(Msg.Length, Msg);
if (retval != 0)
{
// handle error
}
I need to dynamically load DLL and invoke its methods
C code header:
__declspec(dllexport) int Init_Normalization_EN(char* path);
__declspec(dllexport) const char* Process_Normalization_EN(char* input);
C# code using [extern] to statically define library and methods:
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Init_Normalization_EN##YAHPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Init_Normalization_EN(IntPtr path);
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Process_Normalization_EN##YAPBDPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Process_Normalization_EN(IntPtr input);
When these declarations are used, interop works fine (for both init and process of normalization), but I need to point to a DLL dynamically, so I use the following code:
in the class-level:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate int CallInit(IntPtr ipFolder);
private CallInit Init = null;
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate IntPtr CallNormalize(IntPtr ipInput);
private CallNormalize Normalize = null;
in the constructor:
IntPtr pDll = NativeMethods.LoadLibrary(libraryPath);
IntPtr pAddressOfInit = NativeMethods.GetProcAddress(pDll, InitName);
Init = (CallInit)Marshal.GetDelegateForFunctionPointer(pAddressOfInit, typeof(CallInit));
IntPtr pAddressOfNormalize = NativeMethods.GetProcAddress(pDll, NormalizeName);
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(pDll, typeof(CallNormalize));
IntPtr pFolder = Marshal.StringToCoTaskMemAnsi(dataFolderPath);
int result = this.Init(pFolder);
if (result != 0)
{
InitializeCompleted = true;
}
all this code runs OK and even the call to init the normalizer with a folder-path works fine (returns a handle non-zero)
but
when I try to run the text-normalizer:
IntPtr pInput = Marshal.StringToCoTaskMemAnsi(text);
IntPtr pResult = this.Normalize(pInput);
I get on the second line an application-level exception (that cannot be caught by try/catch):
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Which is as far as I can understand caused by the returned string which I try to get as IntPtr as in the [extern] declaration
Shouldn't this line:
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pDll,
typeof(CallNormalize));
be
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pAddressOfNormalize,
typeof(CallNormalize));
I'm writing a .NET wrapper for an unmanaged DLL. The original DLL is C++ with a C shim that basically just replicates the API in C form, so that language bindings aren't such a pain. I've already written a Python binding for it, so I know what I'm trying to do should work.
The DLL has a number of callbacks exported as global members, viz.:
__declspec(dllexport) extern int (*some_callback)(int32_t*, uint32_t);
I need to attach a managed function to this pointer, but I can't figure out how to get at non-function resources in an unmanaged library. Unless I'm just blind, DllImport only imports functions.
Is there a C# way to do this, or do I need to write a little shim DLL in C that provides registration functions? I hate that approach, because it just feels inelegant, but if I have to, I have to.
You're right, P/Invoke cannot handle data exports from a DLL. It is however technically possible by obtaining the export directly. This is very ugly and you'll probably regret it some day, but this worked:
Sample C/C++ DLL:
#include "stdafx.h"
typedef int (__stdcall * pfnCallback)(int*, unsigned*);
extern "C" __declspec(dllexport)
pfnCallback some_callback;
pfnCallback some_callback;
static int theCallback(int*, unsigned*) {
return 42;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) {
some_callback = theCallback;
}
return TRUE;
}
Test C# code:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
class Program {
unsafe static void Main(string[] args) {
IntPtr hMod = LoadLibrary("cpptemp10.dll");
if (hMod == IntPtr.Zero) throw new Win32Exception();
IntPtr export = GetProcAddress(hMod, "some_callback");
if (export == IntPtr.Zero) throw new Win32Exception();
IntPtr callback = Marshal.ReadIntPtr(export);
some_callback dlg = (some_callback)Marshal.GetDelegateForFunctionPointer(callback, typeof(some_callback));
int retval = dlg(null, null);
Console.WriteLine(retval);
Console.ReadLine();
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate int some_callback(int* arg1, uint* arg2);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string path);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hMod, string name);
}