Quick question (hopefully), how do I properly call the win32 function CreateProfile() from C# (managed code)? I have tried to devise a solution on my own with no avail.
The syntax for CreateProfile() is:
HRESULT WINAPI CreateProfile(
__in LPCWSTR pszUserSid,
__in LPCWSTR pszUserName,
__out LPWSTR pszProfilePath,
__in DWORD cchProfilePath
);
The supporting documents can be found in the MSDN library.
The code I have so far is posted below.
DLL Import:
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int CreateProfile(
[MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath,
uint cchProfilePath);
Invoking the function:
/* Assume that a user has been created using: net user TestUser password /ADD */
// Get the SID for the user TestUser
NTAccount acct = new NTAccount("TestUser");
SecurityIdentifier si = (SecurityIdentifier)acct.Translate(typeof(SecurityIdentifier));
String sidString = si.ToString();
// Create string buffer
StringBuilder pathBuf = new StringBuilder(260);
uint pathLen = (uint)pathBuf.Capacity;
// Invoke function
int result = CreateProfile(sidString, "TestUser", pathBuf, pathLen);
The problem is that no user profile is ever created and CreateProfile() returns an error code of: 0x800706f7. Any helpful information on this matter is more than welcomed.
Thanks,
-Sean
Update:
Solved!
string buffer for pszProfilePath cannot have a length greater than 260.
For the out parameter you should set the marshalling. More importantly, by passing a StringBuilder you already implicitly have an output parameter. Thus, it should become:
[DllImport("userenv.dll", CharSet = CharSet.Auto)]
private static extern int CreateProfile(
[MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath,
uint cchProfilePath);
Calling this method:
int MAX_PATH = 260;
StringBuilder pathBuf = new StringBuilder(MAX_PATH);
uint pathLen = (uint)pathBuf.Capacity;
int result = CreateProfile(sidString, "TestUser", pathBuf, pathLen);
It may not be the only problem but you need to add the [Out] attribute to the pszProfilePath parameter in your DLL import declaration.
Related
I have some code which retrieves the 128bit NTFS Ids from files at specific paths. Then I attempted to retrieve the file path using this ID. The code works as long as when retrieving the paths I run as admin. This is not going to be possible in production. Unfortunately I am unable to call Marshal.GetLastWin32Error() because the System.AccessViolationException causes the application to completely crash. Below is the code to retrieve the paths.
public const int NO_PERMISSION = 0;
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
[InAttribute()] System.IntPtr hTemplateFile
);
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle OpenFileById(
IntPtr hVolumeHint,
FILE_ID_DESCRIPTOR lpFileId,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwFlagsAndAttributes
);
public enum _FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType,
ExtendedFileIdType,
MaximumFileIdType
}
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_128
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
[FieldOffset(0)]
public byte[] Identifier;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FILE_ID_DESCRIPTOR
{
public uint dwSize;
public _FILE_ID_TYPE Type;
public FILE_ID_128 ExtendedFileId;
}
public static string GetObjectPathFromId(string pathToSection, string hexId)
{
// We need a file handle to the drive we are looking in
using (SafeFileHandle handle = Methods.CreateFile(
pathToSection,
Constants.NO_PERMISSION,
Constants.NO_PERMISSION,
IntPtr.Zero,
Constants.OPEN_EXISTING,
0x02000000 | 0x00000080,
IntPtr.Zero))
{
// Build descriptor
FILE_ID_DESCRIPTOR descriptor = new FILE_ID_DESCRIPTOR();
descriptor.dwSize = (uint)Marshal.SizeOf(descriptor);
descriptor.Type = _FILE_ID_TYPE.ExtendedFileIdType;
descriptor.ExtendedFileId.Identifier = StringToByteArrayFastest(hexId);
using (SafeFileHandle actualFile = OpenFileById(handle.DangerousGetHandle(), descriptor,
Constants.NO_PERMISSION, Constants.NO_PERMISSION,
IntPtr.Zero, 0))
{
if (actualFile.IsInvalid)
return "";
// Buffer for the path, this should be way big enough
int sizeOfBuffer = 1024;
// Allocate a buffer
IntPtr pointer = Marshal.AllocHGlobal(sizeOfBuffer);
uint size = (uint)sizeOfBuffer;
uint returnValue = GetFinalPathNameByHandleW(actualFile.DangerousGetHandle(), pointer, size, 0);
// Copy it into a managed array
byte[] outPut = new byte[sizeOfBuffer];
Marshal.Copy(pointer, outPut, 0, (int)returnValue);
// Decode it
var str = Encoding.Unicode.GetString(outPut);
// Will be an empty string if the call fails
return str;
}
}
}
Again I want to specify - this code works perfectly when running as admin. The files are owned by the user, the user is able to delete, rename and move the files without any additional permissions.
Any help would be greatly appreciated thanks!
Edit1:
I implemented the answer found here How to handle AccessViolationException to successfully catch the exception. However even after doing this Marshal.GetLastWin32Error() returns 0. If anyone has any idea of how I can debug this type of issue please let me know.
Also it's still functioning when I run as admin, just not as a user.
Edit2:
Not sure if it's relevant - library with this code is building for .NET Standard 2.0 - Application using this library code is building for .NET Framework 4.6.2
I'm having a problem with kernal32 Pinvoke functions as they keeps throwing an INVALID_FILE_HANDLE. The program reads the first sector of the current hard disk. I can't see what is wrong with the following code.
class Program
{
const uint GENERIC_READ = 0x80000000;
const uint FILE_SHARE_READ = 0x00000001;
const uint OPEN_EXISTING = 0x00000003;
const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(string Disk, uint Access, uint ShareMode, IntPtr SecurityAttributes, uint CreationDisposition, uint Flags, IntPtr TemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetFilePointer([In] SafeFileHandle Handle, [In] int DistanceToMove, [Out] out int DistanceToMoveHigh, [In] int MoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
unsafe public static extern int ReadFile(SafeFileHandle Handle, [Out] byte[] Buffer, int NumberOfBytesToRead, out int NumberOfBytesRead, IntPtr Overlapped);
unsafe public static void Main(string[] args)
{
string Drive = #"\\.\C";
int SectorSize = 512;
int Sector = 0;
int BytesRead, DistanceToMoveHigh;
byte[] Buffer = new byte[SectorSize];
SafeFileHandle Handle = CreateFile(Drive, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, IntPtr.Zero);
SetFilePointer(Handle, Sector * SectorSize, out DistanceToMoveHigh, 0);
ReadFile(Handle, Buffer, SectorSize, out BytesRead, IntPtr.Zero);
Console.WriteLine(Marshal.GetLastWin32Error()); // It gives 6 which translates to an INVALID_FILE_HANDLE error
Console.ReadKey();
}
}
Your call to CreateFile fails. Of course you cannot know that because you omitted any error checking. Read the documentation. Errors for all three functions that you call are signaled by the return value. Which you ignore.
Your call to CreateFile returns INVALID_HANDLE_VALUE. You need to test for that. When you encounter that, and only then, call GetLastWin32Error. Likely ERROR_ACCESS_DENIED will be returned then.
Passing FILE_FLAG_DELETE_ON_CLOSE is a mistake. Remove that flag.
I believe that the share flags must be FILE_SHARE_READ | FILE_SHARE_WRITE.
The file name must be #"\\.\C:" with a trailing colon.
And you will need the process to be executed elevated.
You use GetLastWin32Error in a wrong way.
The method that fails here is CreateFile and it returns an INVALID_HANDLE_VALUE (indicating that it failed). To determine what went wrong you have to call GetLastWin32Error directly after CreateFile.
When you call it after trying to read, the error is of course ERROR_INVALID_HANDLE (6) as you passed an invalid handle to ReadFile.
If you call GetLastWin32Error directly after the failing CreateFile you get error 2:
The system cannot find the file specified.
That is because the the drive name misses a :
string Drive = #"\\.\C:"; // <- add colon :
I tried with that drive name, but then got the error 32:
The process cannot access the file because it is being used by another process.
I keep trying to figure out how that can be handled...
The task is to determine the last write time for the registry key. As standard RegistryKey class doesn't provide this feature, I have to use WinAPI function "RegQueryInfoKey". To get the key handle I open it by "RegOpenKeyEx".
This is the WinAPI prototype of the function (taken from MSDN):
LONG WINAPI RegOpenKeyEx(
__in HKEY hKey,
__in LPCTSTR lpSubKey,
DWORD ulOptions,
__in REGSAM samDesired,
__out PHKEY phkResult
);
I use the following declaration:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey, uint samDesired, ref UIntPtr phkResult);
Then I call it in the following way:
UIntPtr hKey = UIntPtr.Zero;
string myKeyName = "blablabla";
UIntPtr HKEY_USERS = (UIntPtr)0x80000003;
uint KEY_READ = 0x20019;
RegOpenKeyEx(HKEY_USERS, myKeyName, KEY_READ, ref hKey);
At this point I get "Access violation" exception. What am I doing wrong?
I think something is wrong with parameters passing, but how to do it right?
Thank you.
There are 5 parameters in the native function's prototype, and only 4 in your P/Invoke signature.
In particular, you're missing DWORD ulOptions. This parameter is "reserved and must be zero" according to the MSDN documentation, but it must still be passed in the function call.
Also, you don't need to set the SetLastError field because the RegOpenKeyEx function returns its error code; you don't have to retrieve it by calling GetLastError. Consequently, you don't need the marshaler to save that value for you automatically. Just check the return value for the error code.
Change your P/Invoke signature to look like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey,
uint ulOptions, uint samDesired,
out UIntPtr phkResult);
The wrong P/Invoke signature is almost always the cause of "access violation" errors. When you see one of those, make sure you double-check it twice!
You've missed ulOptions from your P/Invoke signature.
I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...
The problem below is ralated to my previous question
Converting static link library to dynamic dll
My first step was to develop a dll, that was done. (Thanks John Knoeller prakash. Your input was very helpful)
Now when i call the function in the dll from my c# application i get the error
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Here is the C++ definition
extern "C" DEMO2_API void Decompress(char* inp_buff, unsigned short*
inp_len, char* buffer_decomp,unsigned *output_len,unsigned short* errorCode);
My C# Converstion p/Involke
private static extern void Decompress(
byte[] inp_buff,
ref ushort inp_len,
byte[] buffer_decomp,
ref int output_len,
ref ushort errorCode
);
And I am calling it as below
byte[] dst = new byte[2048];
int outlen = 2048;
ushort errorCode = 0;
Decompress(src, (ushort )src.Length, dst, ref outlen,ref errorCode);
return dst;
What is wrong?
I see a signature mismatch on the inp_len parameter. In the C++ definition you use a pointer to a short unsigned int, while in the C# method you use a ushort.
for pointers you must use IntPtr .net type
#necrostaz
It is not necessary that we use IntPtr for pointers.
Look below all of these four declarations are valid and currently i am using it.
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, String lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, StringBuilder lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, String lParam);
question is still open
In addition to the missing "ref" on the inp_len declaration that Maurits pointed out, you need to make sure that your pointer sizes match.
If you're running on a 32-bit operating system you should be OK, but if your code runs on 64-bit too, then you need to ensure that either:
You mark your .net entry assembly as x86 (not Any CPU)
or
You supply a 32-bit and 64-bit build of the C++ dll and install the correct one for the interop to call.
I have had the same problem two years ago. In my case the reason for the access violation was that the memory was allocated outside the DLL. As a solution I added two functions for memory allocation and deallocation to the DLL.
Another solution could be a change of the .net security settings. Some keywords are "Code Access Security Police Tool" (caspol.exe) and ".NET Framework Configuration Tool" (mscorcfg.msc). In VS there is also a security tab in the project property dialog. I'm not an expert in .net security so someone else should know more details.
The following code runs without any problems. It's very similar to yours:
C++:
extern "C" __declspec(dllexport) void TestFunction(char* inp_buff,
unsigned short* inp_len,
char* buffer_decomp,
unsigned *output_len,
unsigned short* errorCode)
{
//copy input buffer to output buffer
int size = min(*inp_len,*output_len);
for(int i=0; i<size; i++)
buffer_decomp[i] = inp_buff[i];
errorCode = 0;
}
C#:
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("TEST.DLL")]
public static extern void TestFunction(byte[] inp_buff,
ref ushort inp_len,
byte[] out_buff,
ref int out_len,
ref ushort errorCode);
static void Main(string[] args)
{
//prepare input buffer
byte[] inp_buff = new byte[20];
inp_buff[0] = (byte)'T';
inp_buff[1] = (byte)'E';
inp_buff[2] = (byte)'S';
inp_buff[3] = (byte)'T';
ushort inp_len = (ushort)inp_buff.Length;
//prepare output buffer
byte[] out_buff = new byte[20];
int out_len = out_buff.Length;
ushort errorCode = 0;
TestFunction(inp_buff, ref inp_len, out_buff, ref out_len, ref errorCode);
//see if copying was successful
for(int i=0; i<out_len; i++)
Console.Out.Write(out_buff[i]);
}
}
Try it out. I have taken a look at the open parts of the library you are using. Here is a direct excerpt of the function lzo_decomp:
in = lzo_malloc(IN_LEN);
out = lzo_malloc(OUT_LEN);
wrkmem = lzo_malloc(LZO1Z_999_MEM_COMPRESS);
if (in == NULL || out == NULL || wrkmem == NULL)
{
printf("out of memory\n");
}
in_len = IN_LEN;
lzo_memset(in,0,in_len );
lzo_memset ( out, 0, OUT_LEN );
memcpy ( out, &input_buffer, inp_buff_len);
lzo_free(wrkmem);
lzo_free(out);
lzo_free(in);
r = lzo1z_decompress(out,*inp_len,in,&out_len,NULL );
For serenity: "in" and "out" are not the function arguments for the input and output buffers but temporary pointers. What can you see (beside from bad formatted code)? The only two buffers lzo1z_decompress is called with are "in" and "out". And these two buffers are freed before the call. I'm not surprised that there is an access violation. I only can underline nobugz's advice: Contact the author.
The 4th parameter need to be passed using out mode instead of ref. That solved the problem.