Function description here.
I'm struggling to get it right to call this function from c#.
I'm at a stage where I'm calling it but it's returning E_INVALIDARG.
I've set it up as follows...
[DllImport("p2p.dll", CharSet=CharSet.Unicode)]
internal static extern uint PeerGroupCreateInvitation(IntPtr hGroup, string pwzIdentityInfo, IntPtr pftExpiration, int cRoles, IntPtr pRoles, out string ppwzInvitation);
My best guess is the 5th parameter, "pRoles".
I'm supposed to send it a pointer to one or two GUIDs representing the role type.
PEER_GROUP_ROLE_ADMIN
PEER_GROUP_ROLE_MEMBER
I have no clue presently how to do this from c#.
In C this parameter looks like this when calling the function...
..., (PEER_ROLE_ID*) &PEER_GROUP_ROLE_MEMBER, ...
PEER_ROLE_ID looks like a System.Guid type.
PEER_GROUP_ROLE_MEMBER looks like the actual GUID. (Can I get this from the p2p.dll file?)
Any help would be greatly appreciated... especially since there's close to ZERO info on this function on the internet.
Working solution after everyone's comments.
Declaration:
[DllImport("p2p.dll")]
public static extern uint PeerGroupCreateInvitation(IntPtr hGroup, [MarshalAs(UnmanagedType.BStr)] string pwzIdentityInfo, int pftExpiration, int cRoles, ref Guid pRoles, out IntPtr ppwzInvitation);
Calling:
uint hr = PeerGroupCreateInvitation(hGroup, identityInfo, 0, 1, ref PEER_GROUP_ROLE_MEMBER, out pInvitation);
...where PEER_GROUP_ROLE_MEMBER is the System.Guid for this role.
Getting the invitation:
string invitation = Marshal.PtrToStringAuto(pInvitation);
This is the correct declaration:
[DllImport("p2p.dll")]
public static extern uint PeerGroupCreateInvitation(
IntPtr hGroup, /* Updated with #RedDude's suggestion */
[MarshalAs(UnmanagedType.BStr)] string pwzIdentityInfo,
int pftExpiration, // 32 bit, not 64 bit
int cRoles,
ref Guid pRoles,
out IntPtr ppwzInvitation);
As #strenr has said you should use a ref Guid argument to pass the GUID for the pRoles. However, and you might have already decided against this, have you taken a look at the WCF peer-to-peer support? This would give you most of the peer-to-peer capabilities already wrapped up in a .NET interface?
Take a look here
http://msdn.microsoft.com/en-us/library/system.net.peertopeer.aspx
Related
I am working on a project where I need to PInvoke the secur32!AddSecurityPackageA function, but I am still learning the ins and outs of how to do this by hand and could use some help.
Here are a the references I am working with:
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/nf-sspi-addsecuritypackagea
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-_security_package_options
And here's a sample of my code where I am trying to define the struct and call the function:
[DllImport("secur32.dll", EntryPoint = "AddSecurityPackageA")]
public static extern void AddSecurityPackageA(
ref string pszPackageName,
ref SECURITY_PACKAGE_OPTIONS[] Options
);
[StructLayout(LayoutKind.Sequential, CharSet =CharSet.Ansi)]
public class SECURITY_PACKAGE_OPTIONS
{
public ulong Size;
public ulong Type;
public ulong Flags;
public ulong SignatureSize;
public IntPtr Signature;
}
string dll = #"c:\temp\test.dll";
SECURITY_PACKAGE_OPTIONS[] pkgOpts = new SECURITY_PACKAGE_OPTIONS();
AddSecurityPackageA(ref dll, ref pkgOpts);
My questions are:
At lines 3 and 4, is this an appropriate use of ref and is this generally correct according to the MSDN docs?
At line 14, the C++ struct on MSDN has this as a void pointer, but while researching I found that the C# equivalent is an IntPtr. Is that correct or do I need to use unsafe?
In general, has anyone found any really good PInvoke tutorials outside of reading other people's code? I'm moving over from Python so it is quite a bit different and much of what I've found is either "draw a circle, draw the rest of the owl" or insanely lengthy MSDN documentation that makes a lot of assumptions.
Thank you!
Some comments:
Use the W function rather than the A function. You don't want to limit yourself to ANSI. This is a Unicode world.
The function has a return value. You must declare the function with a matching return value type. Presumably it is uint or int but you should check in the C++ header file.
ref string is wrong. It should be string.
ref SECURITY_PACKAGE_OPTIONS[] is wrong. It is not an array. It is a pointer to a struct. Since you declared SECURITY_PACKAGE_OPTIONS as a class, a reference type, you can replace ref SECURITY_PACKAGE_OPTIONS[] with SECURITY_PACKAGE_OPTIONS.
C++ unsigned long is 32 bits, so it should be uint in C#.
IntPtr is correct, but that leaves unresolved the question of how to declare the digital signature and obtain a pointer to it. I think it's outside the remit of this question for us to track down an example of how to do that.
This one works for me:
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint AddSecurityPackage(
string pszPackageName,
SECURITY_PACKAGE_OPTIONS Options
);
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint DeleteSecurityPackage(
string pszPackageName
);
I am writing a piece of program that generates Offline Domain Join blob and saves it for future use. This action can be done using command prompt. Below is a sample command that will generate the mentioned file and save it on D drive:
D:\djoin.exe /REUSE /PROVISION /DOMAIN MyDomain.MyCompany.com /MACHINE "user1-pc" /SAVEFILE blob.txt
More information: Offline Domain Join (Djoin.exe) Step-by-Step Guide
Now, I want to add a method to my program (written with C#) to does this functionality for me.
One of the problems here is, the API that Microsoft has provided is a C++ API. I have tried to use the API in managed code using PInvoke. Below is the code I have written.
using System;
using System.Runtime.InteropServices;
namespace TestBlob
{
public class Program
{
static void Main(string[] args)
{
String domain = "MyDomain.MyCompany.com";
String machineName = "user1-pc";
String machineAccoutOU = null;
String dcName = "MyDomain";
uint options = 1;
IntPtr provisionBinData = IntPtr.Zero;
IntPtr provisionBinDataSize = IntPtr.Zero;
string blob = string.Empty;
IntPtr pProvisionTextData = Marshal.StringToHGlobalUni(blob);
uint status = ODJNativeMethods.NetProvisionComputerAccount(domain, machineName, machineAccoutOU, dcName, options, ref provisionBinData, ref provisionBinDataSize, ref pProvisionTextData);
Console.WriteLine(status);
Console.WriteLine(Marshal.PtrToStringUni(pProvisionTextData));
Console.Read();
}
}
public static class NativeMethods
{
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint NetProvisionComputerAccount([In] String lpDomain,
[In] String lpMachineName,
[In] String lpMachineAccountOU,
[In] String lpDcName,
[In] uint dwOptions,
[In] [Out] ref IntPtr pProvisionBinData,
[In] [Out] ref IntPtr pdwProvisionBinDataSize,
[In] [Out] ref IntPtr pProvisionTextData);
}
}
When I run the application, it always returns 87 (shows on console), which after a quick search turns out to be an error message: The parameter is invalid.
What am I doing wrong here? Are my PInvoke types not the correct ones corresponding to native language API?
The 3 last parameters are declared out, which means you must not initialize them, but pass correct pointer so the function can allocate things for you.
Also, from what I understand reading the function doc, the binary one and the string one are mutually exclusive, so let's say you want to get back the binary one, then you can define the API like this ([in] are usually implicit):
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
public static extern int NetProvisionComputerAccount(
string lpDomain,
string lpMachineName,
string lpMachineAccountOU,
string lpDcName,
int dwOptions,
out IntPtr pProvisionBinData,
out int pdwProvisionBinDataSize,
IntPtr pProvisionTextData);
Note the function does not use SetLastError, so don't declare it in the declaration.
And here is how to call it:
string domain = "MyDomain.MyCompany.com";
string machineName = "user1-pc";
string machineAccoutOU = null;
string dcName = "MyDomain";
// I suggest you don't use hardcoded values to be nice with readers
const int NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 1;
int status = NetProvisionComputerAccount(
domain,
machineName,
machineAccoutOU,
dcName,
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT,
out IntPtr binData, // let the system allocate the binary thing
out int binDataSize, // this will be the size of the binary thing
IntPtr.Zero); // we don't use this one here, pass null
I can't test further (I get error 1354 which I suppose is normal in my context).
Note the doc doesn't say anything about deallocating what the function allocates (if it allocates something? there are some rare Windows API that use static buffers they own). I think you're supposed to call NetApiBufferFree on binData once all work is done, but it's just a guess.
It could be the encoding of your text file - if you have saved it as ANSI and consuming it as Unicode, it might not work.
Use these statements corresponding to your above code:
IntPtr pProvisionTextData = Marshal.StringToHGlobalAnsi(blob);
and
[DllImport("user32", CharSet=CharSet::Ansi)]
Hope it helps.
I'm having trouble interoperating with a DLL written in C. I'm not sure what type of param to put in place of void*
This is how the API of given DLL looks like:
POSNET_API POSNET_STATUS __stdcall POS_SetDeviceParam ( POSNET_HANDLE hDevice,
unsigned long paramCode,
void * paramValue
)
this is how I was trying to import it in C#:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, int POSNET_DEV_PARAM_IP, *type* paramValue);
in place of type I was putting:
[MarshalAs(UnmanagedType.LPStr)] string and other L*Str
[MarshalAs(UnmanagedType.LPArray)] char[] and other type of arrays including of type byte
IntPtr which where AllocHGlobal, GCHandle.Alloc allocated before
even preceeding the method with unsafe and type = void*, combining above alloc aso.
raw type: string, char[]...
I ran out of possibilities by myself.
paramValue should have a value of an IP in following format: "192.168.1.1" - this is how it looks like in a C demo:
string ip="10.33.44.6";
POS_SetDeviceParam(hDevice,POSNET_DEV_PARAM_IP,(void*)ip.c_str());
In C# code, the hDevice is not being initialized - POS_SetDeviceParam should initialize it with additional params.
Any suggestions are very welcome!
You will need to use this P/Invoke signature:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(
IntPtr hDevice,
int paramCode,
IntPtr paramValue
);
But you'll have to do some work to that string in order to pass it through the IntPtr paramValue argument.
Perhaps you can try using Marshal.StringToHGlobalAnsi() as that will give you an IntPtr you can use. If using this method though be sure to free the memory once you've finished with it.
Thank you guys for all the suggestions, the code below solved my problem!
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, UInt32 POSNET_DEV_PARAM_IP, IntPtr paramValue);
void Test() {
POS_SetDeviceParam(new IntPtr(), 0x00020005, Marshal.StringToHGlobalAnsi("192.168.1.1"));
}
I am trying to call some legacy C code using interop in C#.
I am not too famliar with how interop works on C# yet but has to work with some confusing structs.
I got part of it working but the address messes up when I try to get the struct into the C layer.
I am trying to pass a struct to C code, it will do something to it, and I need to get a result back
I have these structs in C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records; //this is the struct RECORD
public IntPtr infoA; // this is the struct INFO
public IntPtr infoB;
public int number;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct INFO
{
public IntPtr doc; //this is a handle in C code
public int cpFirst;
public int cpLim;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECORD
{
public int size;
}
Records is actually a pointer to another Struct STATS defined in C# like this,
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATS
{
public int size;
public int a;
public int b;
public int c;
public int d;
public int e;
public int f;
public int g;
}
in the C# layer, i create the struct like the following
RETURNBUFFER returnBuffer = new RETURNBUFFER();
returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS)));
When I run my code, I was only able to retrieve the first item in returnBuffer which is returnBuffer.records,
all the other item including the int value in returnBuffer is messed up.
I try to debug through it and look into the address value,
I found that when the code codes from C# -> C the address is shifted
I am not sure why the address is off,
Here is an example of what happened under a 64bit environment
C#
&ReturnBuffer
0x00000000046f05f8
&ReturnBuffer.records
0x00000000046f05f8
&ReturnBuffer.infoA
0x00000000046f0600
&ReturnBuffer.infoB
0x00000000046f0608
&ReturnBuffer.number
0x00000000046f0610
in C, let say the function I am calling is taking parameter RETURNBUFFER *pReturnBuffer,
i get these address,
pReturnBuffer
0x00000000046F05F8
&pReturnBuffer->records
0x00000000046F05F8
&pReturnBuffer->infoA
0x00000000046F0600
&pReturnBuffer->infoB
0x00000000046F0610 **This address is off by 8**
&pReturnBuffer->number
0x00000000046F0620 **this is off by 16**
So as a result,
when the code moves back to C# function,
I can construct the returnBuffer.records correctly,
but weren't able to construct neither infoA nor infoB nor get the correct value for returnBuffer.number
not sure what I am missing out here.
===============================================
I edited my code with help of, Fun Mun Pieng
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CRB
{
[FieldOffset(0)]
public IntPtr pcstat;//CSTAT
[FieldOffset(8)]
public IntPtr caProc;//NLCA
[FieldOffset(24)]
public IntPtr caSent;//NLCA
[FieldOffset(40)]
public int cnlch;
}
now, the address matches up when it goes to C# -> C++ -> C#
However I am still getting some garbage data back.
I did some investigation and here is the erratic behaviour I found.
in C# code i make the call like this
IntPtr text = Marshal.StringToCoTaskMemUni("I am here");
legacyFunction(text, ref returnBuffer)
in here, when I call the GetHashCode function, i get the following values
returnBuffer.records.GetHashCode() 473881344
returnBuffer.infoA.GetHashCode() 473898944
returnBuffer.infoB.GetHashCode() 473898784
text.GetHashCode() 468770816
upon returning from the function, these hash value changes,
returnBuffer.records.GetHashCode() 543431240
returnBuffer.infoA.GetHashCode() 473799988
returnBuffer.infoB.GetHashCode() 473799988
text.GetHashCode() 473799988
Now, I can actually do,
Marshal.PtrToStringUni(checkReturnBuffer.infoA) and I get "I am here"
C# now thinks, both infoA and infoB are the same as text .
====================================================
2nd edit
The c++ structure is in fact
typedef struct RETURNBUFFER
{
RECORD *precord;
INFO infoA;
INFO infoB;
UINT number;
} CRB;
Thanks for the reply, this was indeed my problem.
I was somehow under the impression,
for every struct/class/object in C++
I have to make an equivalent IntPtr in C#
One last question, while I am here so I dont have to re define all structs in a new question,
for the IntPtr in struct INFO.
in C++, it is of type HANDLE
Am i correct here to define it as IntPtr? It is only a handle, but it is not a *handle thought, or should i just let it be a uint value?
Here's what I read from a msdn site "Remember, any API function that returns or accepts a handle is really working with an opaque pointer. Your code should marshal handles in Windows as System.IntPtr values"
If i defined it as IntPtr,
How should I alloc memory for it? Will the below be correct?
returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);
to unmarshal
Marshal.PtrToStructure(returnBuffer.infoA.doc, typeof(IntPtr));
is this the right approach?
Thank you so much
It might be because any of the following:
your C++ is compiled to 16 bytes alignment
your type for infoA is different between C++ and C#, or the size of the types are different
possible solutions include:
[FieldOffset(24)] public IntPtr infoB;
OR
comparing IntPtr.Size against sizeof(infoB) on C++
Edit: It seems infoA is 16 bytes, but your INFO declaration is not 16 bytes. It's very likely that your C# and C++ declarations are different. It would be good if you can include your C++ declarations in the question.
In the meantime, I can only guess the best match to be:
public struct RETURNBUFFER
{
public RECORD records; //this is the struct RECORD
public INFO infoA; // this is the struct INFO
public INFO infoB;
public int number;
}
Your assumption that RETURNBUFFER contains structure pointers has to be wrong. That's the only way that infoA and infoB can take up 16 bytes. The INFO structure certainly is 16 bytes long, I can't see the type of infoB. Thus:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records;
public INFO infoA;
public DUNNO infoB;
public int number;
}
Update your question with the C declarations if you still have trouble. It should be easy to see from them.
Try to make sure the win32 struct and c# struct is bit(bit size) mapping. This can be achieved by using exact c# type for win32 type.
I have two communicating components - one managed, the other unmanaged. The managed needs to retrieve a character string from the unmanaged implementation (the same string or just a copy). I tried the following code.
// Unmanaged code
const char* GetTestName(Test* test)
{
return test->getName();
}
// Managed wrapper
[DllImport(DllName, EntryPoint = "GetTestName")]
public static extern IntPtr GetTestName(IntPtr testObj);
// API Invocation
IntPtr testName = GetTestName(test);
string testStr = Marshal.PtrToStringAuto(testName);
But, the value of testStr is not what is expected. Does anyone know what I'm doing wrong here? Any suggestions would be really helpful.
You're close but you have to use PtrToStringAnsi(). Auto uses the system default which will be Unicode.
I'd suggest this, instead:
[DllImport(DllName, EntryPoint = "EntryPoint")]
[MarshalAs(UnmanagedType.LPStr)]
public static extern StringBuilder GetTestName(IntPtr testObj);
UnmanagedType.LPStr works with strings and System.Text.StringBuilder, and perhaps others (I only ever used those two). I've found StringBuilder to work more consistantly, though.
See this MSDN article for further information on the various string marshalling options.