I have an unmanaged MFC application. I have written a CLI wrapper for the application and converted into a DLL. The unamanged code has string table resources that is used to display messages here and there.
Now I can call the unmanaged code from C# and use the internal logic. But the code errors out when it tries to read a resource string. I hope the resources are not loaded so I tried including a P/invoke LoadLibraryEx from kernel32.dll but still no use. How can I let the unmanaged code use its resource file? Can it do that or it should be modifed??
Thanks.
You can rebuilt MFC using UNICODE strings, declare exported function, whick takes a language id, an ID of the resource string, and return string for that reference ID and locale.
And use it in managed assembly as follows
[DllImport("resource.en-US.dll")]
string GetResourceString(int LANGUAGE_ID, int IID);
And, for example:
try
{
...
}
catch(MyException ex)
{
throw new ApplicationException(GetResourceString(ex.Language, ex.ResourceID), ex)
}
Related
I am trying to export code from a function in C# to Delphi. I used DllExport with stdcall convention and made some tests :it works. I tested for integers and strings as params. The problem occurs when I try to use a class from Aforge library in C#. Delphi returns an error: "External Exception E0434F4D".
public class ProcessClass
{
[DllExport(CallingConvention=CallingConvention.StdCall)]
public static void ProcessImage()
{
try
{
Erosion erosion = new Erosion();
}
catch (Exception Ex)
{
}
}
}
Delphi code:
procedure ProcessImage; stdcall; external 'C:\Users\Reznicencu Bogdan\source\repos\OCR\OCR\bin\x86\Release\OCR.dll';
procedure TForm1.FormCreate(Sender: TObject);
begin
ProcessImage;
end;
The error apears at Erosion.Actually every function I use from Aforge library generates an error. I used Try/catch because I know Delphi can't catch exceptions from unmanaged code but it still doesn't work.
How can I solve this error?
The event log says: First chance exception at $767EDDC2. Exception class EExternalException with message 'External exception E0434F4D'. Process Project1.exe (9528)
The problem appears to be related to the location of the AForge DLLs. If you place those, and your class library DLL, in the same directory as your Delphi executable then the error disappears.
Perhaps there is some other way to tell AForge where to locate its DLLs. I will leave that as an exercise for the reader to investigate.
I have a problem with invoking a few functions from DLL (SDK of some camera). In source of .dll, there is function:
NET_SDK_API LONG CALL_METHOD NET_SDK_Login(char *sDVRIP,WORD wDVRPort,char *sUserName,char *sPassword,LPNET_SDK_DEVICEINFO lpDeviceInfo);
and I am trying to call it from .Net console app with following code:
[STAThread]
static void Main(string[] args)
{
long userid = 0;
_net_sdk_deviceinfo dinfo = new _net_sdk_deviceinfo();
short port = 6036;
try
{
if (DVR.NET_SDK_Init())
{
Console.WriteLine("ok");
userid = DVR.NET_SDK_Login("192.168.1.132", port, "admin", "123456", out dinfo);
userid.ToString();
}
else
{
Console.WriteLine("err");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey();
}
i get following error:
A call to PInvoke function 'DVRtest!DVRtest.DVR::NET_SDK_Login' has
unbalanced the stack. This is likely because the managed PInvoke
signature does not match the unmanaged target signature. Check that
the calling convention and parameters of the PInvoke signature match
the target unmanaged signature.
Init passes fine but i cant get anything else. I tried dozen of solutions and I'm getting nowhere.
Here is source of dll and source of my .Net app. Thanks!
[edit]
As #david pointed out, CallingConvention was wrong and now, i get following error:
The runtime has encountered a fatal error. The address of the error
was at 0x6fda02c7, on thread 0x2554. The error code is 0xc0000005.
This error may be a bug in the CLR or in the unsafe or non-verifiable
portions of user code. Common sources of this bug include user
marshaling errors for COM-interop or PInvoke, which may corrupt the
stack.
Is this error from DLL or CLR (.Net)? I never imported any functions from DLL to .Net, so any help is appreciated.
From the unmanaged source:
#define CALL_METHOD __stdcall
And from the managed source:
[DllImport("DVR_NET_SDK.dll", CallingConvention = CallingConvention.Cdecl)]
Your calling conventions do not match.
As for the edit to the question, that is presumably because the C# struct definition does not match the unmanaged struct. You have failed to translate any of the arrays correctly. They will require the use of [MarshalAs(UnmanagedType.ByValArray, SizeConst=...)].
I have made a plugin system that uses reflection to call functions in a plugin. A plugin has to implement the IPlugin interface to be used.
In the application which uses the plugins the plugin instance is created with the following code:
Assembly currentAssembly = Assembly.LoadFrom(startInfo.PluginAssemblyPath);
Type[] types = currentAssembly.GetTypes();
IPlugin pluginInstance = null;
foreach (Type type in types)
{
if (type.FullName == startInfo.PluginTypeName)
{
pluginInstance = (IPlugin)Activator.CreateInstance(type);
}
}
if (pluginInstance == null)
{
throw new Exception("Plugin loader error: Could not instantiate plugin: " + startInfo.ToString()); }
return pluginInstance;
I have made a plugin that uses some unmannaged dll's. When I call the IPlugin interface functions in a test project in the plugin solution everything works fine. But when I call the plugin via the plugin instance made in the code shown above I get the System.AccessViolationException: Attempted to read or write protected memory error when calling functions in the unmannaged dll's.
The unmannaged dll's are c++ dll's made by a third party. I tried enabling native code debugging but i do not have the .pdb files.
I am not sure why this is happening, is this because of the reflection? Or can there be other causes?
Edit:
In the stack I can see the unmannaged function being called:
[MarshalAs(UnmanagedType.LPStr)]
private readonly StringBuilder sb = new StringBuilder(256);
[DllImport("x.dll", EntryPoint = "xLib")]
static extern int _xLib(int a1, int a2, int a3, int a4, int a5, int a6, [Out]StringBuilder str);
The exception is thrown when calling the _xLib function.
Edit: Somewhere in this _xLib function the following function is called:
handle = x_Open();
which is in an other dll and is defined as:
DllExport x_Handle *x_Open();
As soon as anything in the handle is used like:
"%s", handle->x.string
The exception is thrown.
I still do not understand why this is working in the test project and not when I am using it in the app as a plugin.
Maybe you have to pin the StringBuilder to allow unmanaged code to interact with it.
Pinned object is one that is not allowed to move. The garbage collector is normally compacting the memory in that it moves all objects to "one or more clusters". This is to create large chunks of free space.
This basically means if someone else (outside) has a pointer to the memory address of an object, this may point to random content - as the object has moved.
Pinning an object tells the GC to NOT MOVE IT. This is normally useless and ONLY makes sense when working with pointers - like when using PInvoke... and I can see a pointer to a StringBuilder instance in _xlib function
Well after some intensive debugging I found the problem was the handle having some wrong address which caused the violation. The cause of the address being wrong was the x_open function loaded yet another dll with the LoadLibraryA function. This dll was not in the same directory as the executable file so it was not found.
I solved it by adding the directory of this last dll to the environment path.
I'm calling C++ COM interface from C# code using a dll.
At the C++ side, i have a WCHAR* global variable which is updated through a method with a BSTR parameter.
The problem is that, when i first call the C++ wrapper method from C# to change the variable, everything works fine, but at the moment that i call another C++ wrapper method from C#, unexplainably the WCHAR* global variable points to a different memory position and its value gets corrupted.
Some code:
//THE C# side:
capture.filename = PATH + "\\" + DIRECTORY_NAME + "\\";
capture.MaxMinutesPerFile = MAX_MINUTE_PER_FILE;
"capture" is an object of the C++ wrapper class (i think it is autogenerated when building the C++ code to a DLL. Not my code).
"filename" property calls a "put_FileName" C++ method and "MaxMinutesPerFile" a "put_MaxMinutesPerFile" method.
//C++ code
WCHAR *m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
LeaveCriticalSection(&m_critsec);
return hr;
}
STDMETHODIMP CCaptureMF::put_MaxMinutesPerFile(LONG Minutes)
{
MaxMinutes= Minutes;
return S_OK;
}
So, after calling "put_FileName", "m_bstFileName" is updated correctly with the "PathName" value, but just after calling "MaxMinutesPerFile" (or any other interface wrapper method), "m_bstFileName" gets corrupted pointing to a different memory position and fulfilled with strange data.
Thank you.
EDIT:
To make a buffer of "m_bstFileName" and then copy the "PathName" data, i used the following code, taking in mind that "m_bstFileName" size can change at runtime:
m_bstFileName = (wchar_t*)malloc(sizeof(PathName));
wcscpy(m_bstFileName, PathName);
That code works fine, but the rest of the program behaves bad. I´m not sure why, i should investigate more, but for now, could you analyze that pice of code and tell me if it is correct or if i should implement it in other way?
SOLUTION:
Ok, following your recomendations i have finally implemented the following code, which works perfect for the whole application:
CComBSTR m_bstFileName = L"None";
(...)
STDMETHODIMP CCaptureMF::put_FileName(BSTR PathName)
{
EnterCriticalSection(&m_critsec);
HRESULT hr = S_OK;
m_bstFileName = PathName;
if (g_pCapture)
{
g_pCapture->SetPath(m_bstFileName);
}
LeaveCriticalSection(&m_critsec);
return hr;
}
If you think that this can be implemented better, just tell.
Thank you for your help!
In the most basic case you'll need to make a buffer to do a string copy into. Same operation could be accomplished via assignment with cstring, ccombstr, std::string, etc. depending on the framework you're using.
You need to copy the string to m_bstFileName, not just assign it. Use something like
strcpy(m_bstFileName, PathName);
I've been requested to create a .Net dll for an old delphi program. I'm trying to do this with a COM Callable Wrapper, but I keep getting an error when it tries to load the dll (pretty general, something like "I couldn't load the dll"). Here is what the technical documentation says:
The DLL only needs to export one function under the name 'AUTHORIZE'.
function Authorize(InXml: PChar): PChar; stdcall;
(Delphi syntax. May be different in other languages.)
Here is my code for the CCW:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ComCallableWrapper
{
[Guid("C3FD922A-FB44-47B1-9C0C-8F7FAF57098B")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IAuthorizer
{
[DispId(1)]
string Authorize(string lnpInXml);
}
[ProgId("ComCallableWrapper.Authorizer")]
[ClassInterface(ClassInterfaceType.None)]
public class Authorizer : IAuthorizer
{
public Authorizer()
{
}
public string Authorize(string lnpInXml)
{
return "Approved!";
}
}
}
I also run this command "regasm /tlb:ComCallableWrapper.tlb ComCallableWrapper.dll /codebase" on the computer where the delphi program is running.
I've been doing some research on google about how delphi invokes functions on a dll, and I found at least 2 ways:
function Authorize(lnpInXml: pchar): pchar; stdcall; external 'DLLName.dll';
and
oleObject := CreateOleObject('ComCallableWrapper.Authorizer');
ShowMessage(oleObject.Authorize('Approved?'));
It looks like COM works a little bit different. Is there a way to change my CCW to work like the first way?
Regards.
You con't need COM. And indeed using COM is a mistake because the Delphi program is not looking for a COM DLL.
What you need to do is to export an unmanaged function from your managed C# DLL. That's a little tricky and is in fact not supported. These are your most attractive options:
Use Robert Giesecke's UnmanagedExports.
Write a mixed mode C++/CLI DLL that consumes your C# code. The mixed mode C++/CLI is capable of export native functions using __declspec(dllexport), .def files etc.
If you chose to use UnmanagedExports, the function would look like this:
[DllExport]
public static IntPtr Authorize(string InXml)
{
// your code goes here, for now return the input value
return Marshal.StringToHGlobalAnsi(InXml);
}
Implementing the function is a little tricky because you need to return a Delphi PAnsiChar, that is a C++ char*. You cannot use string for the return type and have to use IntPtr. But how do you allocate the string so that it remains valid for the caller to use it. The code above leaks the string in an HGLOBAL.
I can't advise you definitively how to resolve the lifetime of the string. The interface you are coding to is not at all well designed. Only you with more knowledge of the interface are in a position to resolve that issue.