I'm translating a program from VB to C# which communicates with an external program MMTTY.EXE. This program has ActiveX controls for VB6.0 and I added to my C# project in Visual Studio 2013.
The code in VB is this:
Dim m_nmmr(63) As Long
Private Sub XMMR_OnNotifyNMMR(pNMMR As Long)
Call CopyMemory(m_nmmr(0), pNMMR, 64 * 4) 'Windows API CopyMemory()*
This event is produced when MMTTY.EXE have data and the data pointed by pNMMR is copied in m_nmmr(63) buffer.
The program in C# that I made is this:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
static extern void CopyMemory(Int32[] Destination, IntPtr Source, uint Length);
private void XMMR_OnNotifyNMMR(object sender, AxXMMTLib._DXMMREvents_OnNotifyNMMREvent e)
{
IntPtr ptr = (IntPtr)e.pNMMR;
Int32[] m_nmmr = new Int32[63];
Marshal.Copy(ptr, m_nmmr, 0, 63);
}*
But when I execute it I get an AccessViolationException. It tells me that there is an attempt of writing or reading in a protected memory.
How can I solve this problem? any idea?
This is the help of the original method for VB6.0:
void OnNotifyNMMR(long* pNMMR)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This event is generated at the timing of TXM_LEVLEL message arrival. NMMR struct pointed by pNMMR is defined by
#pragma pack(push, 1)
typedef struct {
DWORD m_markfreq;
DWORD m_spacefreq;
DWORD m_siglevel;
DWORD m_sqlevel;
DWORD m_codeswitch;
DWORD m_codeview;
DWORD m_notch1;
DWORD m_notch2;
DWORD m_baud;
DWORD m_fig;
DWORD m_radiofreq;
DWORD m_Reserved[53];
}NMMR;
#pragma pack(pop)
the application does not have to respond all the XMMR events if it uses this struct. Because NMMR struct simply consists of LONG variables, it can be copied to VB Long array. The index of the array has several names, such as xr_markfreq, in XMMT.ocx. For details, refer to Predefined constants of XMMR.
[Example]
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Dim m_nmmr(63) As Long
Private Sub XMMR_OnNotifyNMMR(pNMMR As Long)
Call CopyMemory(m_nmmr(0), pNMMR, 64 * 4) 'Windows API CopyMemory()
|
MarkFreq = m_nmmr(xr_markfreq)
SpaceFreq = m_nmmr(xr_spacefreq)
|
'Pass pNMMR to the control for the supplemental control
Call XMMSpec.UpdateByNMMR(pNMMR) 'Update the frequency property of XMMSpec control
Call XMMLvl.DrawByNMMR(pNMMR) 'Draw the level indicator
End Sub
The 63 is the Problem:
In VB6, 63 is to top index (0..63).
in C#, 63 is the COUNT of elements (0..62).
So you miss 4 Bytes. You can simple check this with a small console program, breakpoint at first assignment:
static void Main(string[] args)
{
int[] test = new int[1];
test[0] = 1;
test[1] = 2; // <- this will crash
}
Related
I am trying to call a non exported function from a native C++ DLL into a C# program.
I have the function signature, which is of type typedef void (_cdecl* TfFunc)(int, unsigned char** data)
The dll is in "A.dll", at offset 0x00003e89d.
In C++ I'd do this :
int handle = LoadLibrary("A.dll");
TfFunc func = (TfFunc)((handle) + 0x00003e89d);
func(1, null);
However despite searching extensively, I can't find a way to do such a thing in C#
I could solve it with that :
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void Func(int a1, out IntPtr a2);
IntPtr handle = LoadLibrary("A.dll");
Func f = Marshal.GetDelegateForFunctionPointer<Func>(new IntPtr(handle + 0x00003e89d));
IntPtr a2 = IntPtr.Zero;
f(1, out a2);
Works as expected
I am working on a project and currently have the following structure:
C# WPF project containing the User Interface as well as calls to external methods.
C++ DLL project containing an algorithm.
ASM DLL project containing an algorithm.
For simplicity, let's assume the algorithm simply takes no parameters and returns the sum of two, predefined numbers.
Here's the function signature and implementation in the C++ (second) project:
int Add(int x, int y)
{
return x + y;
}
extern "C" __declspec(dllexport) int RunCpp()
{
int x = 1, y = 2;
int z = Add(x, y);
return z;
}
And here's how I call the function in C#:
[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunCpp();
This works just fine - calling the function in C# returns the value 3, everything is working proplerly, no exceptions thrown.
However, I am now struggling to call the ASM procedure in C# code.
I have seen (and tested myself to an extent) that it's impossible to call a MASM DLL directly in C# code. However, I've heard that it's possible to call ASM in C++ and call that function in C#.
1. My first question is - is calling ASM code actually possible directly in C#? When I try that, I get an exception that basically says the binary code is incompatible.
2. I have tried to use C++ to indirectly call the ASM DLL, and while I get no exception, the returned value is "random", as in, it feels like a remainder left in memory, for example: -7514271. Is this something I'm doing wrong, or is there another way to achieve this?
Here's the code for calling ASM in C++:
typedef int(__stdcall* f_MyProc1)(DWORD, DWORD);
extern "C" __declspec(dllexport) int RunAsm()
{
HINSTANCE hGetProcIDDLL = LoadLibrary(L"Algorithm.Asm.dll");
if (hGetProcIDDLL == NULL)
{
return 0;
}
f_MyProc1 MyProc1 = (f_MyProc1)GetProcAddress(hGetProcIDDLL, "MyProc1");
if (!MyProc1)
{
return 0;
}
int x = 1, y = 2;
int z = MyProc1(x, y);
FreeLibrary(hGetProcIDDLL);
return z;
}
Here, the code for calling C++ in C#:
[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunAsm();
And here's the ASM code of MyProc1, if needed:
Main.asm:
MyProc1 proc x: DWORD, y: DWORD
mov EAX, x
mov ECX, y
add EAX, ECX
ret
MyProc1 endp
Main.def:
LIBRARY Main
EXPORTS MyProc1
is calling ASM code actually possible directly in C#?
Example of this with two projects, C# and assembly based DLL. Looks like you already know how to get a C++ based DLL working. The project names are the same as the directory names, xcs for C# and xcadll for the dll. I started with empty directories and created empty projects, then moved source files into the directories and then added existing items to each project.
xcadll properties:
Configuration Type: Dynamic Library (.dll)
Linker | Input: xcadll.def
xcadll\xcadll.def:
LIBRARY xcadll
EXPORTS DllMain
EXPORTS Example
xcadll\xa.asm properties (for release build, /Zi is not needed):
General | Excluded From Build: No
General | Item Type: Custom Build Tool
Custom Build Tool | General | Command Line: ml64 /c /Zi /Fo$(OutDir)\xa.obj xa.asm
Custom Build Tool | General | Outputs: $(OutDir)\xa.obj
xcadll\xa.asm:
includelib msvcrtd
includelib oldnames ;optional
.data
.data?
.code
public DllMain
public Example
DllMain proc ;return true
mov rax, 1
ret 0
DllMain endp
Example proc ;[rcx] = 0123456789abcdefh
mov rax, 0123456789abcdefh
mov [rcx],rax
ret 0
Example endp
end
xcs\Program.cs:
using System;
using System.Runtime.InteropServices;
namespace xcadll
{
class Program
{
[DllImport("c:\\xcadll\\x64\\release\\xcadll.dll")]
static extern void Example(ulong[] data);
static void Main(string[] args)
{
ulong[] data = new ulong[4] {0,0,0,0};
Console.WriteLine("{0:X16}", data[0]);
Example(data);
Console.WriteLine("{0:X16}", data[0]);
return;
}
}
}
For debug, use
[DllImport("c:\\xcadll\\x64\\debug\\xcadll.dll")]
xcs properties | debug | enable native mode debugging (check the box)
I am a newbie on both C# WPF and C++.
Recently, I got an external .dll, which returns a char*, and I want to receive the return value in C# by using DllImport. Then, using str.Split(';') to separate the characters. To the purpose, I created a button to show the first character in the string I split on a label when I click the button.
Therefore, I use an IntPtr to receive a char*, which is from C++ .dll, and call Marshal.PtrToStringAnsi() to turn it into a string. However, when I execute the code, it sometimes works but sometimes crushes. Then error code always shows
Unhandled exception at 0x00007FFC06839269 (ntdll.dll) in UITest.exe: 0xC0000374: heap corruption (parameters: 0x00007FFC068A27F0).
I thought my code is reasonable and I couldn't find root causes. Can anyone help me? Thanks!
The below shows the content in .dll and also the code I used in C#.
C++ code in Dlltest.h:
#define DLL_EXPORT extern "C" __declspec(dllexport)
char* getRbtData = nullptr;
DLL_EXPORT char* func_getRbtData();
C++ code in Dlltest.cpp:
char* func_getRbtData()
{
getRbtData = new char(128);
memset(getRbtData, 0, strlen(getRbtData));
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
return getRbtData;
};
C# code in UITest.xaml.cs:
[DllImport("DllTest.dll",EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi)]
public static extern IntPtr func_getRbtData();
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
There are several problems with your code.
On the C++ side, your DLL function is implemented all wrong:
getRbtData = new char(128);
You are allocating a single char whose value is 128, not an array of 128 chars. You need to use new char[128] instead for that.
memset(getRbtData, 0, strlen(getRbtData));
getRbtData is not a pointer to a null-terminated string, so strlen(getRbtData) is undefined behavior. It reads into surrounding memory while calculating the length until it finds a random 0x00 byte in memory.
And then the subsequent memset() into getRbtData will overwrite that surrounding memory. If it doesn't just crash outright.
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
Prior to C++11, this assignment is OK but discouraged. In C++11 and later, this assignment is actually illegal and won't compile.
String literals are read-only data, so you need to use const char instead of char in your pointer type. You should do that even in older compilers.
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
strlen(_getRbtData) is OK since _getRbtData is a pointer to a null-terminated string.
However, since getRbtData is not allocated with enough memory to receive all of the copied chars, memcpy() into getRbtData is also undefined behavior and will trash memory, if not crash outright.
return getRbtData;
This is OK to pass the pointer to C#.
However, since the memory is being allocated with new (better, new[]), it needs to be freed with delete (delete[]), which you are not doing. So you are leaking the memory.
Marshal.PtrToStringAnsi() on the C# side will not (and cannot) free new'ed memory for you. So your C# code will need to pass the pointer back to the DLL so it can delete the memory properly.
Otherwise, you will need to allocate the memory using the Win32 API LocalAlloc() or CoTaskMemAlloc() function so that the Marshal class can be used on the C# side to free the memory directly without passing it back to the DLL at all.
On the C# side, you are using the wrong calling convention on your DllImport statement. The default is StdCall for compatibility with most Win32 API functions. But your DLL function is not specifying any calling convention at all. Most C/C++ compilers will default to __cdecl unless configured differently.
With that said, try this instead:
Dlltest.h
#define DLL_EXPORT extern "C" __declspec(dllexport)
DLL_EXPORT char* func_getRbtData();
DLL_EXPORT void func_freeRbtData(char*);
Dlltest.cpp
char* func_getRbtData()
{
const char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
int len = strlen(_getRbtData);
char *getRbtData = new char[len+1];
// alternatively:
/*
char *getRbtData = (char*) LocalAlloc(LMEM_FIXED, len+1);
if (!getRbtData) return NULL;
*/
memcpy(getRbtData, _getRbtData, len+1);
return getRbtData;
}
void func_freeRbtData(char *p)
{
delete[] p;
// alternatively:
// LocalFree((HLOCAL)p);
}
UITest.xaml.cs
[DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr func_getRbtData();
[DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern void func_freeRbtData(IntPtr p);
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
func_freeRbtData(intptr);
// alternatively:
// Marshal.FreeHGlobal(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
new char(128) returns a pointer to one character, with initial value 128.
I could tell you how to allocate 128 characters, but the chief problem with that is that you can't clean it up so that's not a useful answer. Check the existing questions about returning a string to C#.
I am working with a 3. party SDK, which is made up from .dll, .lib and .h files. I am using the .dll's to PInvoke against. And the .h files to see the function names and parameters. (So I am not using the .lib files).
The SDK is rather complex, so making the PInvoke wrappers have proven to be a challenge. All the functions/structs/enums is defined in the .h files.
My question is how to implement a pinvoke for a function with 2 **.
I expect it is my C# function definition that is wrong.
When I call the function it simplely crashes, no exception throw or anything. The program just stops.
Function: GetInformatiuon(...)
//C Function: GetInformatiuon(...)
ERROR GetInformatiuon(
Component comp,
struct Information** Info);
//C# Function: GetInformatiuon(...)
[DllImport("externalSDK.dll", EntryPoint = "GetInformatiuon", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR GetInformatiuon(Component comp, ref Information Info);
);
Enum: ERROR
//C Enum: ERROR
typedef enum ERROR_E {
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
} ERROR;
//C# Enum: ERROR
public enum ERROR
{
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
}
Struct: Component
//C struct: Component
typedef struct Component_S
{
void* ObjPointer;
unsigned long number;
} Component;
//C# class: Component
[StructLayout(LayoutKind.Sequential)]
public class Component
{
public IntPtr ObjPointer;
public uint number; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
}
Struct: Information
//C struct: Information
typedef struct Information_S {
char* language;
unsigned long sampleFrequency;
unsigned long frameShift;
}Information;
//C# struct: Information
[StructLayout(LayoutKind.Sequential)]
public struct Information
{
public string language;
public uint sampleFrequency; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
public uint frameShift; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
}
I found the solution! (aka Hans Passant comment)
I changed the function to:
Function: GetInformatiuon(...)
//C Function: GetInformatiuon(...)
ERROR GetInformatiuon(
Component comp,
struct Information** Info);
//C# Function: GetInformatiuon(...)
[DllImport("externalSDK.dll", EntryPoint = "GetInformatiuon", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR GetInformatiuon(Component comp, out IntPtr InfoPtr);
);
Calling the function
IntPtr infoPtr;
lhErr = SDK.GetInformatiuon(component, out infoPtr);
Information info = (Information)Marshal.PtrToStructure(infoPtr, typeof(Information)) ;
I need to print image in device with windows mobile 6.5 (with printer) through CF 2.0 and i have c++ header file and i also wrapped class that call unmanaged code:
Problem: I cant figure out how can i print images even if I read this document
In documentation
PRNAPI UINT WINAPI PrinterLoadImageFile (LPCTSTR pszFile);
Description: Read the Image files. Return: PRINTER_OK: Success
PRINTER_ERROR: Errors Argument: LPCTSTR pszFile: [in] file to read
PRNAPI UINT WINAPI PrinterImage (int nMode); Description: Print
image. Return: PRINTER_OK: Success PRINTER_ERROR: Errors Argument:
int nMode: [in] set the image printing mode. PRINTER_IMAGE_NORMAL:
200 * 200 dpi Default PRINTER_IMAGE_DOUBLEWIDTH: 100 * 200 dpi
PRINTER_IMAGE_DOUBLEHEIGHT: 200 * 100 dpi PRINTER_IMAGE_QUADRUPLE:
100 * 100 dpi
PRNAPI UINT WINAPI PrinterCloseImageFile (); Description: Remove
reading image. Return: PRINTER_OK: Success PRINTER_ERROR: Errors
PRNAPI LPCTSTR WINAPI PrinterGetImageName (); Description: Get
the name of image that is read. Return: LPCTSTR: [out] the name of
the file
and i did come with this wrapper .net code
[DllImport(#"PRN_DLL.dll")]
public static extern uint PrinterCloseImageFile();
[DllImport(#"PRN_DLL.dll")]
public static extern uint PrinterLoadImageFile(string pszFile);
[DllImport(#"PRN_DLL.dll")]
public static extern uint PrinterImage(int nMode);
[DllImport(#"PRN_DLL.dll")]
public static extern char[] PrinterGetImageName();
part of h file:
//Close Image File
_DLL_EXPORT_ UINT WINAPI PrinterCloseImageFile();
//Load Image File
_DLL_EXPORT_ UINT WINAPI PrinterLoadImageFile(TCHAR* pszFile);
_DLL_EXPORT_ void WINAPI PrinterSetImageLeft(UINT nImageLeft);//ÇöÀç ´Ü»öºñÆ®¸Ê¸¸ Áö¿ø °¡´ÉÇÔ(2008³â11¿ù)
//Print Image
_DLL_EXPORT_ UINT WINAPI PrinterImage(int nMode);
//Get Image Name
_DLL_EXPORT_ TCHAR* PrinterGetImageName();
When I call this code
String path = PathInfo.GetStartupPath() + "\\logo.png";//Path to image
NativPrinter.PrinterGetImageName();
MessageBox.Show(NativPrinter.PrinterLoadImageFile(path).ToString());
NativPrinter.PrinterImage(NativPrinter.PRINTER_IMAGE_NORMAL);
NativPrinter.PrinterCloseImageFile();
I'm getting error in PrinterLoadImageFile (error code 1000 that mean print error).
So can any one have any clue where is my mistake.
sorry for my English .
The obvious way in which your call to PrinterLoadImageFile could be wrong is that your C# code will pass UTF-16 Unicode text, but perhaps the native library expects 8 bit ANSI. We can't tell because we don't know what TCHAR expands to. If that's it then you'll need to pass an IntPtr to PrinterLoadImageFile and convert to ANSI manually. Use
byte[] ansiBytes = Encoding.Default.GetBytes(path);
byte[] pszPath = new byte[ansiBytes.Length + 1];//+1 for null terminator
ansiBytes.CopyTo(pszPath, 0);
to convert to a null terminated ANSI string, stored in a byte array.
And then copy that to a null-terminated string allocated on the unmanaged heap.
IntPtr ptr = Marshal.AllocHGlobal(pszPath.Length);
Marshal.Copy(pszPath, 0, ptr, pszPath.Length);
You can then pass that on to PrinterLoadImageFile. When you are done with the memory, deallocate it with Marshal.FreeHGlobal.
The other problem is PrinterGetImageName. Almost certainly that returns a pointer to a string that is allocated in the library. So you'd need to declare the return value as IntPtr, and use the Marshal class to convert into a C# string. Your code is going to result in an attempt by the p/invoke marshaller to deallocate the block of memory returned by PrinterGetImageName and I'm sure that's not what you want.