I am trying to pInvoke a C method in c#, but it is giving error;
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
C Method;
HRESULT FilterVolumeInstanceFindFirst(
_In_ LPCWSTR lpVolumeName,
_In_ INSTANCE_INFORMATION_CLASS dwInformationClass,
_Out_ LPVOID lpBuffer,
_In_ DWORD dwBufferSize,
_Out_ LPDWORD lpBytesReturned,
_Out_ LPHANDLE lpVolumeInstanceFind
);
typedef struct _INSTANCE_BASIC_INFORMATION {
ULONG NextEntryOffset;
USHORT InstanceNameLength;
USHORT InstanceNameBufferOffset;
} INSTANCE_BASIC_INFORMATION, *PINSTANCE_BASIC_INFORMATION;
This is my code
[DllImport("FltLib", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
public static extern int FilterVolumeInstanceFindFirst([MarshalAs(UnmanagedType.LPWStr)]
string lpVolumeName,
_INSTANCE_BASIC_INFORMATION dwInformationClass,
// IntPtr dwInformationClass,
out StringBuilder lpBuffer,
int dwBufferSize,
out UInt32 lpBytesReturned,
ref IntPtr lpVolumeInstanceFind);
[StructLayout(LayoutKind.Sequential)]
public struct _INSTANCE_BASIC_INFORMATION
{
public uint NextEntryOffset;
public uint InstanceNameLength;
public uint InstanceNameBufferOffset;
}
and calling code is;
_INSTANCE_BASIC_INFORMATION ins = new _INSTANCE_BASIC_INFORMATION();
StringBuilder sb1 = new StringBuilder();
uint ret = 512;
IntPtr vol = new IntPtr(100);
int res = FilterVolumeInstanceFindFirst("H:", ins, out sb1, 516, out ret, ref vol);
Please help,
Thanks
Your p/invoke translations are wrong. You've got the wrong types in your struct. It should be:
[StructLayout(LayoutKind.Sequential)]
public struct INSTANCE_BASIC_INFORMATION
{
public uint NextEntryOffset;
public ushort InstanceNameLength;
public ushort InstanceNameBufferOffset;
}
And the function itself was somewhat off. It should be:
[DllImport("FltLib")]
public static extern uint FilterVolumeInstanceFindFirst(
[MarshalAs(UnmanagedType.LPWStr)]
string lpVolumeName,
uint dwInformationClass,
out INSTANCE_BASIC_INFORMATION lpBuffer,
uint dwBufferSize,
out uint lpBytesReturned,
out IntPtr lpVolumeInstanceFind
);
The information class is really an enum. Look up its value in the appropriate header file. A quick web search suggests that InstanceBasicInformation has a value of 0. Do check that for yourself though by consulting the header file. Pass the size of the struct as dwBufferSize.
The call should be along these lines:
INSTANCE_BASIC_INFORMATION basicInfo;
uint bytesReturned;
IntPtr volumeInstanceFind;
uint res = FilterVolumeInstanceFindFirst("H:", InstanceBasicInformation,
out basicInfo, (uint)Marshal.SizeOf(typeof(INSTANCE_BASIC_INFORMATION)),
out bytesReturned, volumeInstanceFind);
These translations are always easiest done by writing C++ code first. Then you know what a correct call sequence is without being confounded by erroneous p/invoke translation.
Related
I want to convert c++ code to Delphi. The c++ code is getting stream from camera(Hikvision).I use a dll in my program, I am using this function in dll:
c++ code:
NET_DVR_API LONG __stdcall NET_DVR_RealPlay_V30(LONG lUserID, LPNET_DVR_CLIENTINFO lpClientInfo, void(CALLBACK *fRealDataCallBack_V30) (LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* pUser) = NULL, void* pUser = NULL, BOOL bBlocked = FALSE);
delphi code (that I convert):
type
fRealDataCallBack_V30 =procedure(lRealHandle : LongInt; dwDataType: DWORD;pBuffer: PByte; dwBufSize: DWORD; dwUser: DWORD);stdcall;
function NET_DVR_RealPlay_V30 ( lUserID: Longint; lpClientInfo: LPNET_DVR_CLIENTINFO; fRealDataCallBack_V30: fRealDataCallBack_V30; pUser: Pointer; bBlocked: Boolean): Longint;stdcall; external 'HCNetSDK.dll'name 'NET_DVR_RealPlay_V30';
That function needs callback function:
c++ code :
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* dwUser)
{
printf("\nhello");
}
Delphi code(that I convert):
procedure RealDataCallBack(lRealHandle: Longint; dwDataType: Longword; pBuffer: LPByte; dwBufSize: Longword; pUser:Pointer);stdcall;
begin
ShowMessage('hello');
end;
and use that callback function as a parameter in dll function:
c++ code:
NET_DVR_RealPlay_V30(lUserID,&client, fRealDataCallBack,NULL,false);
delphi code (that I convert):
NET_DVR_RealPlay_V30(iLoginID,#lpClientInfo,RealDataCallBack,nil,false);
my c++ code is work completely correctly,but my delphi code can't call callback function.
All, I was trying to use RtlCopyMemory to duplicate a structure instance, But seems it didn't successfully copy the instance before the callback returns. I didn't know if I missed something, Please help to review the below code. Thanks.
#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
typedef struct _FLT_RELATED_OBJECTS {
USHORT CONST Size;
USHORT CONST TransactionContext; //TxF mini-version
PFLT_FILTER CONST Filter;
PFLT_VOLUME CONST Volume;
PFLT_INSTANCE CONST Instance;
PFILE_OBJECT CONST FileObject;
PKTRANSACTION CONST Transaction;
} FLT_RELATED_OBJECTS, *PFLT_RELATED_OBJECTS;
FLT_POSTOP_CALLBACK_STATUS
CreateBackUpFile_WhenPostCreatedCallback (
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
PFLT_RELATED_OBJECTS copiedRelatedObj;
...
RtlZeroMemory(&copiedRelatedObj, FltObjects->Size);
KdBreakPoint();
RtlCopyMemory(&copiedRelatedObj,FltObjects,FltObjects->Size);
DbgPrint("The file name in the FltObjects is : %s\n",FltObjects->FileObject->FileName);
DbgPrint("The file name in the Duplicated FltObjects is : %s\n",copiedRelatedObj->FileObject->FileName);
...
}
RtlZeroMemory requires pointer to a memory block as its first argument. But you give it pointer to pointer ( as PFLT_RELATED_OBJECTS is already a pointer ). Use
FLT_RELATED_OBJECTS copiedRelatedObj;
PFLT_RELATED_OBJECTS copiedRelatedObj;
The copiedRelatedObj variable is a pointer. It is not initialized. Yell a bit invisible Microsoft C programmers for that dreadful habit of declaring pointer types. Then remove the P. Fix:
FLT_RELATED_OBJECTS copiedRelatedObj;
UPDATE
I have made a test project for the original question (that I moved below).
C# code:
namespace TestManagedCom
{
[ComVisible(true)]
public class DummyObject
{
public void Method1(int value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method1] value={0:X}, hwnd={1}", value, hwnd));
}
public void Method2(long value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method2] value={0:X}, hwnd={1}", value, hwnd));
}
}
}
C++ code:
class CDispatchWrapper : public COleDispatchDriver
{
public:
CDispatchWrapper(){}
CDispatchWrapper(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
CDispatchWrapper(const CDispatchWrapper& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}
void CallMethod(DISPID dwDispID, int value)
{
static BYTE parms[] = VTS_I4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
}
void CallMethod(DISPID dwDispID, long long value)
{
static BYTE parms[] = VTS_I8;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
};
};
template <typename T>
void Execute(const CString& progId, const CString& methodName, T value)
{
LPDISPATCH lpEventComponent = NULL;
_com_ptr_t<_com_IIID<IDispatch, &IID_IDispatch> > pCreateComp;
HRESULT hr = pCreateComp.CreateInstance(progId);
if(SUCCEEDED(hr) && pCreateComp != NULL)
{
hr = pCreateComp.QueryInterface(IID_IDispatch, (void**)&lpEventComponent);
if(SUCCEEDED(hr))
{
USES_CONVERSION;
DISPID dwFunctionID = 0;
OLECHAR FAR *szFunc = T2OLE(const_cast<LPTSTR>(methodName.GetString()));
hr = lpEventComponent->GetIDsOfNames(IID_NULL, &szFunc, 1, LOCALE_SYSTEM_DEFAULT, &dwFunctionID);
if(SUCCEEDED(hr) && dwFunctionID != -1)
{
lpEventComponent->AddRef(); // released by the dispatch driver
CDispatchWrapper wrapper(lpEventComponent);
wrapper.CallMethod(dwFunctionID, value);
}
}
}
}
Execute<int>(_T("TestManagedCom.DummyObject"), _T("Method1"), 0x11223344);
Execute<long long>(_T("TestManagedCom.DummyObject"), _T("Method2"), 0x1122334455667788LL);
It works well when the target is x64. It prints:
[Method1] value=11223344, hwnd=287454020
[Method2] value=1122334455667788, hwnd=1234605616436508552
The call to Method2 throws an exeption when the target is x86.
First-chance exception at 0x76A2B727 in TestOleDispatcher.exe:
Microsoft C++ exception: EEException at memory location 0x003FE3C4.
If there is a handler for this exception, the program may be safely
continued.
I have tried with both long long and __int64 and the error is obviously the same.
It seems that somehow it cannot correctly marshall VTS_I8 params on x86.
The original question
I have problems in some legacy code calling a method in a .NET class that represents a COM object with COleDispatchDriver::InvokeHelper. One of the parameters is the handle of a window.
The .NET code used to look like this (simplified):
[ComVisible(true)]
public class Sample
{
public void Method1(int hwndParent)
{
}
}
And the C++ code
class CSendEventWrapper : public COleDispatchDriver
{
public:
void CallMethod(DISPID dwDispID, long* hwnd)
{
static BYTE parms[] = VTS_PI4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
};
HWND hWnd = ...;
long lval = (long)hWnd;
o.CallMethod(dispId, &lval); // snippet for calling the method
This worked OK when the C++ app was 32-bit only. But on a 64-bit version, this is not correct, since HWND is 64-bit and long is just 32-bit, so you lose data.
So I started changing the .NET code to use IntPtr instead of int (as it should have been in the first place).
[ComVisible(true)]
public class Sample
{
public void Method1(IntPtr hwndParent)
{
}
}
But now the problem is how do I call it with InvokeHelper. I tried doing something like this:
void CallMethod(DISPID dwDispID, INT_PTR hwnd)
{
#ifdef _WIN64
static BYTE parms[] = VTS_PI8;
#else
static BYTE parms[] = VTS_PI4;
#endif
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
HWND hWnd = ...;
INT_PTR lval = (INT_PTR)hWnd; // 32- or 64-bit depending on the platform
o.CallMethod(dispId, &lval); // snippet for calling the method
However, this now results in an exception that says a parameter was in an incorrect format. IntPtr should be 32-bit or 64-bit depending on whether the process if 32-bit or 64-bit. I'm not sure what's wrong.
Any help for figuring how to correctly pass the HWND with InvokeHelper both for 32-bit and 64-bit versions is appreciated. (And no, I cannot replace the use of COleDispatchDriver).
Looks like you have mismatch of parameter types. Getting the handle from c# typically gives you the window handle in a IntPtr. This will be the actual handle, not a pointer to the handle. From your code it looks like you're expecting a pointer to handle. I can tell by the long* hWnd and VTS_PI4.
If the COM call really wants a INT_PTR (a pointer to the handle), you'll have to store the variable passed in and take the address of it to pass on. If it takes the window handle directly, then you'll need to modify the VTS_PI4/VTS_PI8 to VTS_I4/VTS_I8.
I am working on MSIL profiler and encountered problems with ManagedToUnmanagedTransition and UnmanagedToManagedTransition callbacks of ICorProfilerCallback interface.
What I want to retrieve is an information about method being called (name and module name it resides in).
So far it was working fine. Until so called dynamic pinvoke occured (described in detail at: http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx)
In this scenario IMetaDataImport::GetPinvokeMap fails. Also IMetaDataAssemblyImport::GetAssemblyProps returns "dynamic_pinvoke" as a name of the assembly.
profiler_1_0->GetTokenAndMetaDataFromFunction(function_id, IID_IMetaDataImport, (IUnknown**) &imd_import, &md_token);
imd_import->GetPinvokeMap(md_token, &mapping, module_name, buffer_size, &chars_read, &md_module_ref);
// here the fail occurs
profiler_1_0->GetTokenAndMetaDataFromFunction(function_id, IID_IMetaDataAssemblyImport, (IUnknown**) &imd_assembly_import, &md_token);
imd_assembly_import->GetAssemblyFromScope(&md_assembly);
imd_assembly_import->GetAssemblyProps(md_assembly, 0, 0, 0, assembly_name, buffer_size, &chars_read, 0, 0);
// assembly_name is set to "dynamic_pinvoke"
How to obtain a module name (.dll) and a name of function being pinvoked through dynamic pinvoke?
The profiler APIs are returning metadata specified in the managed code, normally via the DllImportAttribute. In the case of the 'dynamic pinvoke' which uses the Marshal.GetDelegateForFunctionPointer method, the module and function names were never specified as metadata and not available. An alternative approach to dynamic pinvoke declarations that includes the required metadata will probably avoid this issue. Try using System.Reflection.Emit APIs such as TypeBuilder.DefinePInvokeMethod as one solution.
Here is an example using System.Reflection.Emit that does work with the profiler APIs.
using System;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Reflection;
namespace DynamicCodeCSharp
{
class Program
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private delegate int MessageBoxFunc(IntPtr hWnd, string text, string caption, int options);
static readonly Type[] MessageBoxArgTypes = new Type[] { typeof(IntPtr), typeof(string), typeof(string), typeof(int)};
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
static MethodInfo BuildMessageBoxPInvoke(string module, string proc)
{
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(module), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(module);
TypeBuilder typeBuilder = moduleBuilder.DefineType(proc);
typeBuilder.DefinePInvokeMethod(proc, module, proc,
MethodAttributes.Static | MethodAttributes.PinvokeImpl,
CallingConventions.Standard, typeof
(int), MessageBoxArgTypes,
CallingConvention.StdCall, CharSet.Auto);
Type type = typeBuilder.CreateType();
return type.GetMethod(proc, BindingFlags.Static | BindingFlags.NonPublic); ;
}
static MessageBoxFunc CreateFunc()
{
MethodInfo methodInfo = BuildMessageBoxPInvoke("user32.dll", "MessageBox");
return (MessageBoxFunc)Delegate.CreateDelegate(typeof(MessageBoxFunc), methodInfo);
}
static void Main(string[] args)
{
MessageBoxFunc func = CreateFunc();
func(IntPtr.Zero, "Hello World", "From C#", 0);
}
}
}
A few examples to demonstrate the issues with the current approach.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);
static void Main(string[] args)
{
MessageBox(IntPtr.Zero, "Hello World", "From C#", 0);
}
There is no MessageBox function exported from user32.dll. It only contains MessageBoxA and MessageBoxW. As we did not specify the ExactSpelling=false in the DllImport attribute and our CharSet is Unicode, .Net will also search user32.dll for our entry point appended with a W. This means MessageBoxW is in fact the native function we are calling. However, GetPinvokeMap returns 'MessageBox' as the function name (module_name variable in your code).
Now lets instead invoke the function via ordinal number rather than name. Using the dumpbin program in the Windows SDK:
dumpbin /exports C:\Windows\SysWOW64\user32.dll
...
2046 215 0006FD3F MessageBoxW
...
2046 is the ordinal number for MessageBoxW. Adjusting our DllImport declaration to use the EntryPoint field we get:
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "#2046")]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);
This time GetPInvokeMap returns "#2046". We can see the profiler knows nothing about the 'name' of the native function being invoked.
Going even further, the native code being called may not even have a name. In the following example, an 'Add' function is created in executable memory at runtime. No function name or library has ever been associated with the native code being executed.
using System;
using System.Runtime.InteropServices;
namespace DynamicCodeCSharp
{
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int AddFunc(int a, int b);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr VirtualAlloc(IntPtr addr, IntPtr size, int allocType, int protectType);
const int MEM_COMMIT = 0x1000;
const int MEM_RESERVE = 0x2000;
const int PAGE_EXECUTE_READWRITE = 0x40;
static readonly byte[] buf =
{
// push ebp
0x55,
// mov ebp, esp
0x8b, 0xec,
// mov eax, [ebp + 8]
0x8b, 0x45, 0x08,
// add eax, [ebp + 8]
0x03, 0x45, 0x0c,
// pop ebp
0x5d,
// ret
0xc3
};
static AddFunc CreateFunc()
{
// allocate some executable memory
IntPtr code = VirtualAlloc(IntPtr.Zero, (IntPtr)buf.Length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// copy our add function implementation into the memory
Marshal.Copy(buf, 0, code, buf.Length);
// create a delegate to this executable memory
return (AddFunc)Marshal.GetDelegateForFunctionPointer(code, typeof(AddFunc));
}
static void Main(string[] args)
{
AddFunc func = CreateFunc();
int value = func(10, 20);
Console.WriteLine(value);
}
}
}
I am using function pointers and LoadLibrary. Following is the my code. When I added EvtExportLog after _EvtSubscribe call, it started corrupting _EvtSubscribe values, If i added it before as done in following code everything works fine, now When I add another function pointer I am facing the same problem, anybody know what could be the issue here.
HMODULE module = LoadLibrary(L"wevtapi.dll");
_EvtExportLog = (BOOL (WINAPI * )(EVT_HANDLE ,LPCWSTR ,LPCWSTR ,LPCWSTR ,DWORD ))GetProcAddress(module, "EvtExportLog");
_EvtClearLog = (BOOL (WINAPI * )(EVT_HANDLE ,LPCWSTR,LPCWSTR,DWORD))GetProcAddress(module, "EvtClearLog");
_EvtOpenLog = (EVT_HANDLE (WINAPI *)(EVT_HANDLE ,LPCWSTR ,DWORD ))GetProcAddress(module, "EvtOpenLog");
_EvtGetLogInfo = (BOOL (WINAPI * )( EVT_HANDLE, EVT_LOG_PROPERTY_ID, DWORD, PEVT_VARIANT ,PDWORD))GetProcAddress(module, "EvtGetLogInfo");
_EvtQuery = (EVT_HANDLE (WINAPI * )(EVT_HANDLE,LPCWSTR ,LPCWSTR ,DWORD))GetProcAddress(module, "EvtQuery");
_EvtNext = (BOOL (WINAPI * )(EVT_HANDLE ,DWORD,EVT_HANDLE*,DWORD,DWORD,PDWORD))GetProcAddress(module, "EvtNext");
_EvtClose = (BOOL (WINAPI *)(EVT_HANDLE))GetProcAddress(module, "EvtClose");
_EvtCreateRenderContext = (EVT_HANDLE (WINAPI *)(DWORD, LPCWSTR *, DWORD))GetProcAddress(module, "EvtCreateRenderContext");
_EvtFormatMessage = (BOOL (WINAPI *)(EVT_HANDLE, EVT_HANDLE, DWORD, DWORD, PEVT_VARIANT, DWORD, DWORD, LPWSTR, PDWORD))GetProcAddress(module, "EvtFormatMessage");
_EvtOpenPublisherMetadata = (EVT_HANDLE (WINAPI *)(EVT_HANDLE, LPCWSTR, LPCWSTR, LCID, DWORD))GetProcAddress(module, "EvtOpenPublisherMetadata");
_EvtRender = (BOOL (WINAPI *)(EVT_HANDLE, EVT_HANDLE, DWORD, DWORD, PVOID, PDWORD, PDWORD))GetProcAddress(module, "EvtRender");
_EvtSubscribe = (EVT_HANDLE (WINAPI *)(EVT_HANDLE, HANDLE, LPCWSTR, LPCWSTR, EVT_HANDLE, PVOID, EVT_SUBSCRIBE_CALLBACK, DWORD))GetProcAddress(module, "EvtSubscribe");
This has nothing to do with DLL Load order, you're trashing the stack somewhere else, and it just happens that _EvtSubscribe is the victim depending on how you order the objects on the stack. The easiest way to trash the stack is if you were calling a function with the wrong signature, possibly by transcribing them by hand instead of just using static linking and the header.