I have been reading many threads about this but I can't fix it.
I'm trying to import this function (originally written in C++)
__declspec( dllexport ) int __stdcall GeekFunction(void *indices,
unsigned int *vertexRemap,
unsigned int numIndices,
unsigned int numVertices,
int indexFormat,
void *allocator);
To my C# project:
[DllImport("sce_psp2vertexcache.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "GeekFunction")]
unsafe private static extern int GeekFunction(
[In, Out] IntPtr indices,
[Out] IntPtr vertexRemap,
[In] UInt32 numIndices,
[In] UInt32 numVertices,
[In] int indexFormat,
[In] void* allocator);
I am calling the function this way:
UInt32[] vertexRemap = new UInt32[locs.data.Length * 6];
GCHandle handleVertexRemap = GCHandle.Alloc(vertexRemap, GCHandleType.Pinned);
GCHandle handleIndexdata = GCHandle.Alloc(indexdata, GCHandleType.Pinned);
if (GeekFunction(
GCHandle.ToIntPtr(handleIndexdata),
GCHandle.ToIntPtr(handleVertexRemap),
Convert.ToUInt32(indexdata.Length),
Convert.ToUInt32(locs.data.Length), 1, null) != 0)
StatusOutput.FatalError("Geekfunction Failed");
handleIndexdata.Free();
Am I missing something?
Finally, I have found out what was happening.
I was calling several times to GeekFunction with the same handle:
if (GeekFunction(
GCHandle.ToIntPtr(handleIndexdata),
GCHandle.ToIntPtr(handleVertexRemap),
Convert.ToUInt32(indexdata.Length),
Convert.ToUInt32(locs.data.Length), 1, null) != 0)
StatusOutput.FatalError("Geekfunction Failed");
...
if (GeekFunction(
GCHandle.ToIntPtr(handleIndexdata),
GCHandle.ToIntPtr(handleVertexRemap),
Convert.ToUInt32(indexdata.Length),
Convert.ToUInt32(locs.data.Length), 1, null) != 0)
StatusOutput.FatalError("Geekfunction Failed");
If I rebuild the handle between the calls I don't get the AccessViolationException:
try
{
if (GeekFunction(
handleBoneIndices.AddrOfPinnedObject(),
handleVertexRemap.AddrOfPinnedObject(),
Convert.ToUInt32(boneindices.data.Length),
Convert.ToUInt32(sizeof(VertexData.Index4)), null) != 0)
StatusOutput.FatalError("GeekFunctionFailed: boneIndices");
}
finally
{
handleBoneIndices.Free();
handleVertexRemap.Free();
}
**handleVertexRemap = GCHandle.Alloc(vertexRemap, GCHandleType.Pinned);**
try
{
if (scePsp2VertexCacheApplyVertexRemapping(
handleBoneIndices.AddrOfPinnedObject(),
handleVertexRemap.AddrOfPinnedObject(),
Convert.ToUInt32(boneindices.data.Length),
Convert.ToUInt32(sizeof(VertexData.Index4)), null) != 0)
StatusOutput.FatalError("scePsp2VertexCacheApplyVertexRemapping Failed: boneIndices");
}
finally
{
handleBoneIndices.Free();
handleVertexRemap.Free();
}
thank you for your help. The tests with the simpler function helped a lot.
Related
If I have the following C# DllImport which is importing a non-C# DLL and I want to port it over to Go, how would I go about doing that?
[DllImport("my.dll", EntryPoint = "Greet", CallingConvention = CallingConvention.Cdecl)]
public static extern int Greet(IntPtr name, ref IntPtr greetings);
I've run into problems figuring out how to pass a pointer to a pointer which is needed for the greetings parameter (I assume since the type is ref IntPtr, I'm not that familiar at all with C#). The dll function will populate the memory pointed to by the pointer that I provide which I'll use in subsequent syscalls. Here's what I've got so far,
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
MyDll = syscall.MustLoadDLL("my.dll")
greet = MyDll.MustFindProc("Greet")
)
func Greet(name string) error {
nameP, err := syscall.UTF16PtrFromString(name)
if err != nil {
return err
}
// I need to provide a pointer to a pointer for greetings. How can I allocate some memory here
// then pass a pointer to its pointer? I tried this: create a handle with a zero-value, then
// take a pointer to it, then pass a pointer to the pointer as the second parameter to Call but
// the pointer becomes nil after the Call.
handle := syscall.Handle(0)
handleP := &handle
r1, _, _ := greet.Call(uintptr(unsafe.Pointer(nameP)), uintptr(unsafe.Pointer(&handleP)))
if r1 == 0 {
return fmt.Errorf("there was an error")
}
return nil
}
I'm open to any and all suggestions including links and resources that might help me get a better grasp on this syscall and unsafe stuff. Thanks!
Firstly, it would help if you could show how the C# Greet method is used. A method in isolation its quite hard to understand especially when the parameter is the equivalent of void ** which means anything can go in.
TL;DR
ref IntPtr is probably just a **struct{} where you don't have to allocate any struct. The library will simply manage the memory for you. You just need to give it a pointer to "*MyStruct" so that it can change you "*MyStruct" to actually point to the internal resource.
REF
The C# ref keyword is quite well explained in the docs. Basically it allows a pass by reference for any type.
The following declaration in C#
void Increment(ref value) { ... }
int counter = 10;
Increment(ref counter);
// counter is now 11
Increment(counter); // won't compile must be passed with 'ref'
Increment(null); // won't compile
is equivalent to C++
void Increment(int& value) { ... }
int counter;
Increment(counter);
// counter is now 11
Increment(null); // won't compile
A reference which should not be null.
IntPtr
IntPtr is usually used to represent pointers and allows for interop between native and CLR (C#) programs.
If a C program has the following signature
void Increment(int* value);
a C# program could call it in one of a few ways
[DllImport("example.dll")]
static unsafe extern void Increment(int* value); // this way allows null to be passed in
unsafe {
int counter = 10;
Increment(&10);
}
,
[DllImport("example.dll")]
static extern void Increment(ref int value);
int counter = 10;
Increment(ref counter);
If a C program has the following signature
void AllocateStruct(struct MyStruct** ppStruct);
void IncrementStruct(struct MyStruct* pStruct);
then
[DllImport("example.dll")]
static extern void AllocateStruct(ref IntPtr ppStruct);
// static unsafe extern void AllocateStruct(MyStruct** ppStruct)
[DllImport("example.dll")]
static extern void IncrementStruct(IntPtr pStruct);
// static unsafe extern void IncrementStruct(MyStruct* pStruct);
IntPtr pMyStruct;
AllocateStruct(ref pMyStruct);
IncrementStruct(pMyStruct);
// Free My Struct
// If you need to get inside the struct then
// MyStruct myStruct = Marshal.StructureToPtr<MyStruct>(pMyStruct)
// Often you don't (need to) or (should not) manipulate the struct directly so keeping it as IntPtr is perfectly acceptable.
From the example above you can see MyStruct is more of a token/reference than anything else. ref IntPtr allows you to pass a reference to the location which you will use to store your token/reference after the library allocates it on your behalf. Then all the other methods will usually just use the reference IntPtr to perform subsequent manipulations on it. Basically Object Orientated Programming without classes.
"Real" Life Example with ref IntPtr
It is a bit quick and dirty and error handling leave a lot to be desired.
It shows the C, C# and Go versions which call the same GetSecurityInfo
and LookupAccountSid Win32 library functions.
The only real use case I can find for ref IntPtr is type**/void** so that the library can allocate the memory for you or give you a pointer to memory it has already allocated.
C
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include "accctrl.h"
#include "aclapi.h"
#pragma comment(lib, "advapi32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
// --- get the executing program's file path and handle -----
LPTSTR executablePath = argv[0];
_tprintf(TEXT("Opening File %s\n"), executablePath);
HANDLE hFile = CreateFile(
executablePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) return EXIT_FAILURE;
// -------------------------------------------------
// --------- Get the owner SID of the file ---------
PSID pSidOwner = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD dwRtnCode = GetSecurityInfo(
hFile,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION,
&pSidOwner,
NULL,
NULL,
NULL,
&pSD);
if (dwRtnCode != ERROR_SUCCESS) return EXIT_FAILURE;
// -------------------------------------------------
// -------
TCHAR AcctName[MAX_PATH];
DWORD dwAcctName = MAX_PATH;
TCHAR DomainName[MAX_PATH];
DWORD dwDomainName = MAX_PATH;
SID_NAME_USE eUse = SidTypeUnknown;
BOOL bRtnBool = LookupAccountSid(
NULL, // local computer
pSidOwner,
&AcctName,
&dwAcctName,
DomainName,
&dwDomainName,
&eUse);
if (bRtnBool == FALSE) return EXIT_FAILURE;
_tprintf(TEXT("Account Owner = %s\n"), AcctName);
_tprintf(TEXT("Account Owner's Domain = %s\n"), DomainName);
return 0;
}
C#
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public class Example
{
[DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern uint GetSecurityInfo(
IntPtr handle,
uint ObjectType,
uint SecurityInfo,
ref IntPtr ppsidOwner, // <-- HERE
IntPtr ppsidGroup, // bit hacky (in safe C# you must "pass a reference" in C you can pass a pointer to a pointer or null)
IntPtr ppDacl,
IntPtr ppSacl,
ref IntPtr ppSecurityDescriptor // <-- HERE
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
string lpSystemName,
IntPtr Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out uint peUse);
const uint ERROR_SUCCESS = 0;
const uint OWNER_SECURITY_INFORMATION = 0x00000001;
const uint SE_FILE_OBJECT = 1;
public static void Main()
{
// get the executing program's file path and handle
string executablePath = Environment.GetCommandLineArgs().GetValue(0).ToString();
IntPtr hFile = File.Open(executablePath, FileMode.Open, FileAccess.Read, FileShare.Read)
.SafeFileHandle.DangerousGetHandle();
IntPtr pSidOwner = IntPtr.Zero; // some internal struct you shouldn't allocate or modify (acts like a token)
IntPtr pSD = IntPtr.Zero; // some internal struct you shouldn't allocate or modify (acts like a token)
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
uint dwRtnCode = GetSecurityInfo(
hFile,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION,
ref pSidOwner, // <-- HERE
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
ref pSD // <-- HERE
);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if (dwRtnCode != ERROR_SUCCESS) throw new InvalidOperationException("GetSecurityInfo Failed");
StringBuilder name = new StringBuilder(50);
uint cchName = (uint)name.Capacity;
StringBuilder domainName = new StringBuilder(50);
uint cchDomainName = (uint)domainName.Capacity;
uint sidUse;
LookupAccountSid(
null,
pSidOwner,
name,
ref cchName,
domainName,
ref cchDomainName,
out sidUse);
Console.WriteLine("Account Owner = {0}", name);
Console.WriteLine("Account Owner's Domain = {0}", domainName);
// PLEASE FREE pSD once done
}
}
Go
my second Go program I have ever written so there are probably some blaring mistakes (other than the lack of error checking)
package main
import (
"fmt"
"syscall"
"unsafe"
"os"
)
var (
advapi32, _ = syscall.LoadLibrary("advapi32.dll")
kernel32, _ = syscall.LoadLibrary("kernel32.dll")
createFileW, _ = syscall.GetProcAddress(kernel32, "CreateFileW")
getSecurityInfo, _ = syscall.GetProcAddress(advapi32, "GetSecurityInfo")
lookupAccountSidW, _ = syscall.GetProcAddress(advapi32, "LookupAccountSidW")
)
type SE_OBJECT_TYPE uint32
const (SE_FILE_OBJECT = 1)
type SECURITY_INFORMATION uint32
const (OWNER_SECURITY_INFORMATION = 0x00000001)
const (
GENERIC_READ = 0x80000000
FILE_SHARE_READ = 0x00000001
OPEN_EXISTING = 0x00000003
FILE_ATTRIBUTE_NORMAL = 0x00000080
)
type Handle uintptr
func CreateFile(
name string,
access uint32,
mode uint32,
sa *uint, // *SecurityAttributes,
createmode uint32,
attrs uint32,
templatefile *uint,
) (handle Handle, err error) {
utf16name, _ := syscall.UTF16PtrFromString(name)
r0, _, _ := syscall.Syscall9(
uintptr(createFileW), 7,
uintptr(unsafe.Pointer(utf16name)),
uintptr(access),
uintptr(mode),
uintptr(unsafe.Pointer(sa)),
uintptr(createmode),
uintptr(attrs),
uintptr(unsafe.Pointer(templatefile)),
0, 0)
handle = Handle(r0)
return
}
func GetSecurityInfo(
handle Handle,
objectType SE_OBJECT_TYPE,
securityInformation SECURITY_INFORMATION,
owner **struct{},
group **struct{},
dacl **struct{},
sacl **struct{},
sd **struct{}, //**SECURITY_DESCRIPTOR,
) (ret error) {
r0, _, _ := syscall.Syscall9(
uintptr(getSecurityInfo), 8,
uintptr(handle),
uintptr(objectType),
uintptr(securityInformation),
uintptr(unsafe.Pointer(owner)),
uintptr(unsafe.Pointer(group)),
uintptr(unsafe.Pointer(dacl)),
uintptr(unsafe.Pointer(sacl)),
uintptr(unsafe.Pointer(sd)),
0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func LookupAccountSid(
systemName *uint16,
sid *struct{}, // *SID,
name *uint16,
nameLen *uint32,
refdDomainName *uint16,
refdDomainNameLen *uint32,
use *uint32,
) (err error) {
r, _, e := syscall.Syscall9(
uintptr(lookupAccountSidW), 7,
uintptr(unsafe.Pointer(systemName)),
uintptr(unsafe.Pointer(sid)),
uintptr(unsafe.Pointer(name)),
uintptr(unsafe.Pointer(nameLen)),
uintptr(unsafe.Pointer(refdDomainName)),
uintptr(unsafe.Pointer(refdDomainNameLen)),
uintptr(unsafe.Pointer(use)),
0, 0)
if r == 0 {
err = e
}
return
}
func main() {
defer syscall.FreeLibrary(advapi32)
defer syscall.FreeLibrary(kernel32)
// get the executing program's file path and handle
var hFile, _ = CreateFile(os.Args[0], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nil);
// defer LocalFree(Handle(unsafe.Pointer(pSD))) // PLEASE FREE pSD once done
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
var pSD *struct{} //*SECURITY_DESCRIPTOR
var pSidOwner *struct{}
GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, nil, nil, nil, &pSD)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
nameLen := uint32(50)
name := make([]uint16, nameLen)
domainLen := uint32(50)
domainName := make([]uint16, domainLen)
var sidUse uint32
LookupAccountSid(nil, pSidOwner, &name[0], &nameLen, &domainName[0], &domainLen, &sidUse)
var n = syscall.UTF16ToString(name)
var dn = syscall.UTF16ToString(domainName)
fmt.Printf("Account Owner = %s\n", n)
fmt.Printf("Account Owner's Domain = %s\n", dn)
}
I have PKCS #7 SignedData from an Authenticode-signed PE file. I want to show it in a dialog like this:
This is a standard Windows dialog, like you will see if you click Details on the Digital Signatures tab of a PE file.
Any idea how to do this?
I would prefer a C# solution, but the standard C API would also work (I could make a C++/CLI interface.)
Well it took a lot of research, and a bit of reverse-engineering, but I eventually got it to work.
The magic function is CryptUIDlgViewSignerInfo().
The CryptUIDlgViewSignerInfo function displays a dialog box that contains the signer information for a signed message.
Unfortunately it, along with the definition for its only argument, CRYPTUI_VIEWSIGNERINFO_STRUCT aren't in any header files. So first you need to declare:
CryptUI.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct tagCRYPTUI_VIEWSIGNERINFO_STRUCT {
DWORD dwSize;
HWND hwndParent;
DWORD dwFlags;
LPCTSTR szTitle;
CMSG_SIGNER_INFO *pSignerInfo;
HCRYPTMSG hMsg;
LPCSTR pszOID;
DWORD_PTR dwReserved;
DWORD cStores;
HCERTSTORE *rghStores;
DWORD cPropSheetPages;
LPCPROPSHEETPAGE rgPropSheetPages;
} CRYPTUI_VIEWSIGNERINFO_STRUCT, *PCRYPTUI_VIEWSIGNERINFO_STRUCT;
#ifdef UNICODE
#define CryptUIDlgViewSignerInfo CryptUIDlgViewSignerInfoW
#else
#define CryptUIDlgViewSignerInfo CryptUIDlgViewSignerInfoA
#endif
BOOL WINAPI CryptUIDlgViewSignerInfo(
_In_ CRYPTUI_VIEWSIGNERINFO_STRUCT *pcvsi
);
#ifdef __cplusplus
} // extern "C"
#endif
Now, this is a C++ CLI function, but it should easily work in plain C if you tweak the beginning. Of course, you'll want to do some better error checking as well, but this was just a proof-of-concept:
// Link against these libraries
#pragma comment (lib, "Crypt32")
#pragma comment (lib, "Cryptui")
void CertHelper::DoStuff(array<Byte>^ data) {
// http://stackoverflow.com/questions/17689154
pin_ptr<Byte> pData = &data[0];
CERT_BLOB blob;
blob.cbData = data->Length;
blob.pbData = pData;
BOOL res;
DWORD MsgAndCertEncodingType;
DWORD ContentType;
DWORD FormatType;
HCERTSTORE hCertStore;
HCRYPTMSG hMsg;
res = CryptQueryObject(
CERT_QUERY_OBJECT_BLOB, // dwObjectType [in]
&blob, // pvObject [in]
CERT_QUERY_CONTENT_FLAG_ALL, // dwExpectedContentTypeFlags [in]
CERT_QUERY_FORMAT_FLAG_BINARY, // dwExpectedFormatTypeFlags [in]
0, // dwFlags [in]
&MsgAndCertEncodingType, // pdwMsgAndCertEncodingType [out]
&ContentType, // pdwContentType [out]
&FormatType, // pdwFormatType [out]
&hCertStore, // phCertStore [out]
&hMsg, // phMsg [out]
NULL // ppvContext [out]
);
// Get the SignerInfo - call once to get size
DWORD cb;
res = CryptMsgGetParam(
hMsg, // hCryptMsg [in]
CMSG_SIGNER_INFO_PARAM, // dwParamType [in]
0, // dwIndex [in]
NULL, // pvData [out]
&cb // pcbData [in, out]
);
CMSG_SIGNER_INFO* signerinfo = (CMSG_SIGNER_INFO*)LocalAlloc(LPTR, cb);
res = CryptMsgGetParam(
hMsg, // hCryptMsg [in]
CMSG_SIGNER_INFO_PARAM, // dwParamType [in]
0, // dwIndex [in]
signerinfo, // pvData [out]
&cb // pcbData [in, out]
);
// Initialize the View Signer Info structure
CRYPTUI_VIEWSIGNERINFO_STRUCT vsi;
memset(&vsi, 0, sizeof(vsi));
vsi.dwSize = sizeof(vsi);
vsi.hwndParent = NULL; // TODO
vsi.dwFlags = 0; // SHDocvw.dll passes 0x14
vsi.szTitle = NULL;
vsi.pSignerInfo = signerinfo;
vsi.hMsg = hMsg;
vsi.pszOID = "1.3.6.1.5.5.7.3.3"; // XCN_OID_PKIX_KP_CODE_SIGNING
// Show the dialog already!
res = CryptUIDlgViewSignerInfo(&vsi);
// Free resources
LocalFree(signerinfo);
if (hCertStore) CertCloseStore(hCertStore, 0);
if (hMsg) CryptMsgClose(hMsg);
}
For reference sake, this was RE'd from ViewCertProperties() in shdocvw.dll.
Most of the work leading up to the CryptUIDlgViewSignerInfo call seems to be already done internally by the .NET class System.Security.Cryptography.Pkcs.SignedCms.
The two pieces required to populate the CRYPTUI_VIEWSIGNERINFO_STRUCT are already there, as private fields of SignedCms:
hMsg is SignedCms.m_safeCryptMsgHandle
pSignerInfo is SignerInfo.m_pbCmsgSignerInfo
It would be incredible if we could just call a hypothetical SignedCms.ShowSignerInfoDialog() function, or somehow have access to these members without reflection.
The following hack does however work!
class Program
{
static void Main(string[] args) {
var data = ...;
var cms = new SignedCms();
cms.Decode(data);
var pbCmsgSignerInfo = typeof(SignerInfo).GetField("m_pbCmsgSignerInfo", BindingFlags.NonPublic | BindingFlags.Instance);
var si = (SafeHandle)pbCmsgSignerInfo.GetValue(cms.SignerInfos[0]);
var safeCryptMessageHandle = typeof(SignedCms).GetField("m_safeCryptMsgHandle", BindingFlags.NonPublic | BindingFlags.Instance);
var hMsg = (SafeHandle)safeCryptMessageHandle.GetValue(cms);
var vsi = new CRYPTUI_VIEWSIGNERINFO_STRUCT {
dwSize = (uint)Marshal.SizeOf(typeof(CRYPTUI_VIEWSIGNERINFO_STRUCT)),
pSignerInfo = si,
hMsg = hMsg,
};
CryptUIDlgViewSignerInfo(ref vsi);
}
[DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptUIDlgViewSignerInfo(ref CRYPTUI_VIEWSIGNERINFO_STRUCT pcvsi);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CRYPTUI_VIEWSIGNERINFO_STRUCT
{
public UInt32 dwSize;
public IntPtr hwndParent;
public UInt32 dwFlags;
public string szTitle;
public SafeHandle pSignerInfo;
public SafeHandle hMsg;
[MarshalAs(UnmanagedType.LPStr)]
public string pszOID;
public IntPtr dwReserved;
public UInt32 cStores;
public IntPtr rghStores;
public UInt32 cPropSheetPages;
public IntPtr rgPropSheetPages;
}
I need to invoke a native DLL from C# code. As I am not very familiar with C/C++, I can't figure out how a structure defined in C should be declared in C# so it can be invoked. The problem is that two parameters seems to be an array of structs, which I don't know how to declare this in C# (see last code block):
c++ header file:
typedef enum
{
OK = 0,
//others
} RES
typedef struct
{
unsigned char* pData;
unsigned int length;
} Buffer;
RES SendReceive(uint32 deviceIndex
Buffer* pReq,
Buffer* pResp,
unsigned int* pReceivedLen,
unsigned int* pStatus);
c# declaration:
enum
{
OK = 0,
//others
} RES
struct Buffer
{
public uint Length;
public ??? Data; // <-- I guess it's byte[]
}
[DllImport("somemodule.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint SendReceive(
uint hsmIndex,
uint originatorId,
ushort fmNumber,
??? pReq, // <-- should this be ref Buffer[] ?
uint reserved,
??? pResp, // <-- should this be ref Buffer[] ?
ref uint pReceivedLen,
ref uint pFmStatus);
in an equivalent java client, i found that the parameter is not just one Buffer but an array of Buffers. In C# it would look like this:
var pReq = new Buffer[]
{
new Buffer { Data = new byte[] { 1, 0 }, Length = (uint)2 },
new Buffer {Data = requestStream.ToArray(), Length = (uint)requestStream.ToArray().Length },
//according to the header file, the last item must be {NULL, 0}
new Buffer { Data = null, Length = 0 }
};
var pResp = new Buffer[]
{
new Buffer { Data = new byte[0x1000], Length = 0x1000 },
//according to the header file, the last item must be {NULL, 0}
new Buffer { Data = null, Length = 0x0 }
};
This seems strange to me because the extern C method does have a pointer to a Buffer struct (Buffer*) and not a pointer to a Buffer array (Buffer[]*).
How do I need to define the Struct in C# and the parameter types of the extern method?
Any help appreciated, Thanks.
Firstly your struct has the parameters in the wrong order. And the byte array needs to be declared as IntPtr with manual marshalling:
struct Buffer
{
public IntPtr Data;
public uint Length;
}
The p/invoke should be:
[DllImport("MyNativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern RES SendReceive(
uint deviceIndex,
[In] Buffer[] pReq,
[In, Out] Buffer[] pResp,
out uint pReceivedLen,
out uint pStatus
);
The byte array needs to be IntPtr so that the struct is blittable. And that's needed so that the array parameters can be declared as Buffer[].
It's going to be a bit of a pain doing the marshalling of the byte arrays. You'll want to use GCHandle to pin the managed byte arrays, and call AddrOfPinnedObject() to get the address of the pinned array for each struct in your arrays of structs. It will be worth your while writing some helper functions to make that task less painful.
Your method signature in c# should be something like:
[DllImport("MyNativeDll.dll")]
public static extern RES SendReceive (uint32 deviceIndex, ref Buffer pReq, ref Buffer pResp, ref uint pReceivedLen, ref uint pStatus);
See this project, it might hel you in the future so generate native calls from .net
http://clrinterop.codeplex.com/releases/view/14120
Based on the C++ header but without testing, have a look at the following code:
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace WindowsFormsApplication1
{
public class Class1
{
public struct Buffer
{
[MarshalAs(UnmanagedType.LPStr)]
public StringBuilder pData;
public uint length;
}
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary(string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress(int hModule, string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
static extern bool FreeLibrary(int hModule);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal delegate IntPtr SendReceive(
uint deviceIndex,
ref Buffer pReq,
ref Buffer pResp,
uint pReceivedLen,
uint pStatus);
public void ExecuteExternalDllFunction()
{
int dll = 0;
try
{
dll = LoadLibrary(#"somemodule.dll");
IntPtr address = GetProcAddress(dll, "SendReceive");
uint deviceIndex = 0;
Buffer pReq = new Buffer() { length = 0, pData = new StringBuilder() };
Buffer pResp = new Buffer() { length = 0, pData = new StringBuilder() };
uint pReceivedLen = 0;
uint pStatus = 0;
if (address != IntPtr.Zero)
{
SendReceive sendReceive = (SendReceive)Marshal.GetDelegateForFunctionPointer(address, typeof(SendReceive));
IntPtr ret = sendReceive(deviceIndex, ref pReq, ref pResp, pReceivedLen, pStatus);
}
}
catch (Exception Ex)
{
//handle exception...
}
finally
{
if (dll > 0)
{
FreeLibrary(dll);
}
}
}
}
}
I am trying to PInvoke this function (GetPackageId) from kernel32.dll:
http://msdn.microsoft.com/en-us/library/windows/desktop/hh446607(v=vs.85).aspx
I defined the structs and imports as follows:
[StructLayout(LayoutKind.Sequential)]
public struct PACKAGE_ID
{
uint reserved;
uint processorArchitecture;
PACKAGE_VERSION version;
String name;
String publisher;
String resourceId;
String publisherId;
}
[StructLayout(LayoutKind.Explicit)]
public struct PACKAGE_VERSION
{
[FieldOffset(0)] public UInt64 Version;
[FieldOffset(0)] public ushort Revision;
[FieldOffset(2)] public ushort Build;
[FieldOffset(4)] public ushort Minor;
[FieldOffset(6)] public ushort Major;
}
[DllImport("kernel32.dll", EntryPoint = "GetPackageId", SetLastError = true)]
static extern int GetPackageId(IntPtr hProcess,out uint bufferLength,out PACKAGE_ID pBuffer);
And calling it like this:
PACKAGE_ID buffer = new PACKAGE_ID();
result = GetPackageId(hProcess, out bufferLength, out buffer);
However I get a return value of 122 (ERROR_INSUFFICIENT_BUFFER). I am rather new to PInvoke and am not quite sure how to proceed from here. Do I need to initialize the strings before calling the function?
You are going to need to change the p/invoke:
[DllImport("kernel32.dll", SetLastError=true)]
static extern int GetPackageId(
IntPtr hProcess,
ref int bufferLength,
IntPtr pBuffer
);
You call it once passing 0 for the length:
int len = 0;
int retval = GetPackageId(hProcess, ref len, IntPtr.Zero);
Then you need to check that retval equals ERROR_INSUFFICIENT_BUFFER. If it does not then you have an error.
if (retval != ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception();
Otherwise you can continue.
IntPtr buffer = Marshal.AllocHGlobal(len);
retval = GetPackageId(hProcess, ref len, buffer);
Now you can check retval against ERROR_SUCCESS.
if (retval != ERROR_SUCCESS)
throw new Win32Exception();
And finally we can convert the buffer to a PACKAGE_ID.
PACKAGE_ID packageID = (PACKAGE_ID)Marshal.PtrToStructure(buffer,
typeof(PACKAGE_ID));
Put it all together and it looks like this:
int len = 0;
int retval = GetPackageId(hProcess, ref len, IntPtr.Zero);
if (retval != ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception();
IntPtr buffer = Marshal.AllocHGlobal((int)len);
try
{
retval = GetPackageId(hProcess, ref len, buffer);
if (retval != ERROR_SUCCESS)
throw new Win32Exception();
PACKAGE_ID packageID = (PACKAGE_ID)Marshal.PtrToStructure(buffer,
typeof(PACKAGE_ID));
}
finally
{
Marshal.FreeHGlobal(buffer);
}
From the comments it appears that we also need to make changes to the way the PACKAGE_ID struct is marshalled.
I suggest the following:
[StructLayout(LayoutKind.Sequential)]
public struct PACKAGE_ID
{
uint reserved;
uint processorArchitecture;
PACKAGE_VERSION version;
IntPtr name;
IntPtr publisher;
IntPtr resourceId;
IntPtr publisherId;
}
followed by calls to Marshal.PtrToStringUni to convert the IntPtr string fields into C# strings. Naturally this conversion needs to happen before the call to FreeHGlobal.
My guess is that the API actually allocates the string buffers in the space beyond the end of PACKAGE_ID. Which is why you have to ask how much memory to allocate. I don't have Windows 8 at hand to test this hypothesis.
From the docs for GetPackageId it seems you should send the size of the buffer as argument when calling, i.e. bufferLength should be initialized with the size of the passed buffer.
On return the bufferLength will tell you the size of the returned buffer.
Or did misread the docs?
I'm trying to translate some Code written in C to C# (Compact Framework 2.0)
(It's for a Windows CE Device with an RFID Reader).
And in C this system call works fine, but it does not work in C#:
Code In C
HANDLE m_Power =NULL; //<-- This HANDLE is set correctly before call "ControlDevice"
int ControlDevice(int device, BYTE power_state)
{
bool bRet;
DWORD tcBuffer2,dwBytesReturned;
unsigned int a = sizeof(power_state);
bRet=DeviceIoControl( m_Power,
device,
&power_state,
sizeof(power_state),
(PBYTE)&tcBuffer2,
sizeof(DWORD),
&dwBytesReturned,
NULL);
if(bRet)
return 1;
else
return -1;
}
CODE IN C#
int ControlDevice(int device, byte[] power_state)
{
try
{
bool bRet = false;
int bytesReturned = 0;
byte[] tcBuffer2 = new byte[1];
tcBuffer2[0] = 0;
bRet = Device_WinApi.DeviceIoControlCE(m_Power,
device,
power_state,
(int)Marshal.SizeOf(power_state),
tcBuffer2,
(int)Marshal.SizeOf(tcBuffer2),
out bytesReturned,
IntPtr.Zero);
if (bRet)
return 1;
else
{
int LastError = Device_WinApi.GetLastError();
return -1;
}
}
catch (Exception e)
{
return -1;
}
}
The DLL is imported in this way:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(
IntPtr hDevice,
long dwIoControlCode,
byte[] lpInBuffer,
int nInBufferSize,
byte[] lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
In the current implementation in C#, bRet = false, and I should be true, as this is the value obtained in C
this return zero too:
int LastError = Device_WinApi.GetLastError()
Any help will be really appreciated!
Thank you advance for your time!!