CreateRemoteThread says file doesn't exist, but it DOES exist - c#

I am trying to inject a .dll into another process's memory, by using Interop.
This is my C# code:
class Program
{
static void Main(string[] args)
{
var result = Inject(Process.GetProcessesByName("notepad")[0].Id);
Console.WriteLine(result);
if (result < 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
Console.ReadLine();
}
[DllImport("Testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Inject(int dwProcessId);
}
The code for the function Inject is this (notice the comment on return -6):
//C++ .dll that does actually exists
const char* DLL_NAME = "C:\\Users\\Bruno\\Source\\Repos\\CourseGuidance\\InteropTesting\Debug\\Loader.dll";
TESTING_API DWORD Inject(DWORD dwProcessId)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess == NULL)
return -1;
HMODULE hModule = GetModuleHandleW(L"kernel32.dll");
if (hModule == NULL)
return -2;
FARPROC pLoadLibrary = GetProcAddress(hModule, "LoadLibraryA");
if (pLoadLibrary == NULL)
return -3;
LPVOID pMemory = VirtualAllocEx(hProcess, NULL, strlen(DLL_NAME) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pMemory == NULL)
return -4;
BOOL result = WriteProcessMemory(hProcess, pMemory, DLL_NAME, strlen(DLL_NAME) + 1, NULL);
if (!result)
return -5;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE) pLoadLibrary, pMemory, 0, NULL);
if (hThread == NULL)
return -6;
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, pMemory, strlen(DLL_NAME) + 1, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return 0;
}
I think the error I am getting might be misleading, since the file does exist in that folder. I also thought the error could be because of LoadLibraryA, which is for ASCII, even tried using LoadLibraryW, but I still got the same issue.
If someone has an idea of what could be wrong, could you provide me the right direction?

You did not declare Inject as SetLastError=true. Therefore, the value you got from Marshal.GetLastWin32Error is garbage:
Marshal.GetLastWin32Error Method
Returns the error code returned by the last unmanaged function that
was called using platform invoke that has the
DllImportAttribute.SetLastError flag set.
You can use this method to obtain error codes only if you apply the
System.Runtime.InteropServices.DllImportAttribute to the method
signature and set the SetLastError field to true. The process for this
varies depending upon the source language used: C# and C++ are false
by default, but the Declare statement in Visual Basic is true.

Related

Pass pointer to a pointer in Go DLL Syscall

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)
}

Wrong behaviour when passing string from c# to c dll

I researched a lot about my problem and found many questions relative to [how to pass string from c# to c dll] :
Passing strings from C# to C++ DLL and back -- minimal example
https://www.codeproject.com/Articles/1189085/%2FArticles%2F1189085%2FPassing-strings-between-managed-and-unmanaged-code
But none of them couldn't help me. Any way I prefer to ask my question :
Briefly explanation :
My c function working properly in c# just when I interchange the GetProcessIntegrityLevel parameters (both in c dll and also in c# import-dll) from :
BOOL GetProcessIntegrityLevel(DWORD dwPID, LPSTR szIntegrityLevel);
To this one :
BOOL GetProcessIntegrityLevel(LPSTR szIntegrityLevel, DWORD dwPID);
In 1st state my program working properly and changes the LPSTR szIntegrityLevel But in 2nd, It doesn't work and LPSTR szIntegrityLevel is always empty with any pid.
I created a c dll library in which I declared the following c function that get Integrity Level of a process through a pid :
#define MAX_INTEGRITY_LEVEL_LENGTH 30
extern "C"
{
__declspec(dllexport) BOOL GetProcessIntegrityLevel(DWORD dwPID, LPSTR szIntegrityLevel);
}
C function implementation :
BOOL GetProcessIntegrityLevel(DWORD dwPID, LPSTR szIntegrityLevel)
{
BOOL bFlag = FALSE; // Return flag
HANDLE hToken = INVALID_HANDLE_VALUE; // Token handle
HANDLE hProcess = NULL; // Process handle
BOOL bProcToken = FALSE; // OpenProcessToken() result
BOOL bTokenInfo = FALSE; // GetTokenInformation() result
DWORD dwLengthNeeded = 0; // Token information length
DWORD dwError = ERROR_SUCCESS; // GetTokenInformation() last error
DWORD dwIntegrityLevel = 0; // Integrity level
PTOKEN_MANDATORY_LABEL pTIL = NULL; // Use as token information
// Open the process
hProcess = OpenProcess(MAXIMUM_ALLOWED | PROCESS_QUERY_LIMITED_INFORMATION,
FALSE, dwPID);
if (hProcess != NULL)
{
// Open process token
bProcToken = OpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
if (bProcToken == TRUE)
{
// Get token information
bTokenInfo = GetTokenInformation(hToken, TokenIntegrityLevel,
NULL, 0, &dwLengthNeeded);
if (bTokenInfo == FALSE)
{
dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
pTIL = (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, dwLengthNeeded);
if (pTIL != NULL)
{
// Get token information
bTokenInfo = GetTokenInformation(hToken, TokenIntegrityLevel,
pTIL, dwLengthNeeded, &dwLengthNeeded);
if (bTokenInfo == TRUE)
{
// Get integrity level
dwIntegrityLevel = *GetSidSubAuthority(pTIL->Label.Sid,
(DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1));
if (dwIntegrityLevel <= SECURITY_MANDATORY_LOW_RID)
{
// Low Integrity
StrCpyA(szIntegrityLevel, "Low");
}
else if ((dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) &&
(dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID))
{
// Medium Integrity
StrCpyA(szIntegrityLevel, "Medium");
}
else if ((dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) &&
(dwIntegrityLevel < SECURITY_MANDATORY_SYSTEM_RID))
{
// High Integrity
StrCpyA(szIntegrityLevel, "High");
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_SYSTEM_RID)
{
// System Integrity
StrCpyA(szIntegrityLevel, "System");
}
else if (dwIntegrityLevel == SECURITY_MANDATORY_UNTRUSTED_RID)
{
// Untrusted integrity
StrCpyA(szIntegrityLevel, "Untrusted");
}
else
{
StrCpyA(szIntegrityLevel, "UnKnown");
}
bFlag = TRUE;
}
LocalFree(pTIL);
}
}
}
// Close token handle
CloseHandle(hToken);
}
// Close the process handle
CloseHandle(hProcess);
}
return bFlag;
}
So, I import GetProcessIntegrityLevel() from my c dll in c# as following :
// Define function pointers for using of c dll functions
[DllImport("ProcessesPropDll.dll",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
// Get integrity level
static extern bool GetProcessIntegrityLevel(ulong dwPID, StringBuilder szIntegrityLevel);
And call the function in c# like this :
// Integrity level
StringBuilder integrityLevel = new StringBuilder(200);
if(GetProcessIntegrityLevel(11684, integrityLevel) == true)
{
MessageBox.Show(integrityLevel.ToString());
}
In this state when i run the program, the GetProcessIntegrityLevel() returns true but integrityLevel is always empty in messagebox for any pid!!!!
‌Surprisingly, when I interchange the parameters in my c function and also in c#, It does work :
__declspec(dllexport) BOOL GetProcessIntegrityLevel(LPSTR szIntegrityLevel, DWORD dwPID);
This is very strange for me. Also I tried with MarshalAs(UnmanagedType.LPStr)] but give me the same result.
In C# ulong is a 64 bit type. The PID parameter is DWORD which is a 32 bit parameter.
Change your C# parameter declaration from ulong to uint to fix the problem.

Marshalling wchar_*t via pinvoke in c# does not return its value back to StringBuilder

First of all I want to say that I know enough about c++ and pinvoke to be frustrated.
I am currently working on a c++ wrapper and pinvoke that is not returning the info I need.
Below are the pieces of what I am doing:
I have a c++ method with the signature of:
DllExport int LookupFamilyName(__in PCWSTR familyName,__out_opt wchar_t* result, int lengthIn)
With the internal code:
UINT32 count = 0;
UINT32 length = 0;
LONG rc = GetPackagesByPackageFamily(familyName, &count, NULL, &length, NULL);
if (rc == ERROR_SUCCESS)
{
return int(rc);
}
else if (rc != ERROR_INSUFFICIENT_BUFFER) //122
{
return int(rc);
}
PWSTR *fullNames = (PWSTR *) malloc(count * sizeof(*fullNames));
if (fullNames == NULL)
{
return 700010; //faile to allocate memeory
}
PWSTR buffer = (PWSTR) malloc(length * sizeof(WCHAR));
if (buffer == NULL)
{
return 700011; //faile to allocate memeory
}
rc = GetPackagesByPackageFamily(familyName, &count, fullNames, &length, buffer);
if (rc != ERROR_SUCCESS)
{
return int(rc);
}
else
{
result = new wchar_t[lengthIn];
for (UINT32 i = 0; i < count; ++i)
{
wcscpy_s(result,lengthIn,fullNames[i]);
}
}
free(buffer);
free(fullNames);
I am then using the following to invoke:
[DllImport("ClassLibrary1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int LookupFamilyName(string familyName, ref StringBuilder sbPackageFullName, int len);
And then I am calling it from C# with the following:
string packagename = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe";
StringBuilder showmethemoney = new StringBuilder(64);
int x = LookupFamilyName(packagename, ref showmethemoney,showmethemoney.Capacity);
I'm passing in a StringBuilder for the wchar_t* and that value is filled in with what I want by the time we get to the end of the LookupFamilyName method but when we get back to the c# code, the StringBuilder does not have the value that the wchar_t* had in the c++ code.
For those of you that know far more then I, what do I need to do to make sure that the the value in the wchar_t* result gets passed back to the StringBuilder?

Convert IntPtr to char** in C#

I'd like to interpret the output of the following unmanaged function:
afc_error_t afc_get_device_info (afc_client_t client, char ***device_information)
I import the dll with the code:
[DllImport("libimobiledevice.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern short afc_get_device_info(IntPtr client, out IntPtr info);
As long as I only needed to convert response to string Marshal.PtrToStringAnsi was okay. However I have no idea how to convert that IntPtr back to char array.
It should be something like:
IntPtr di;
int result = afc_read_directory(client, #"C:\", out di);
if (di == IntPtr.Zero)
{
throw new Exception();
}
IntPtr di2 = di;
while (true)
{
IntPtr ptr = Marshal.ReadIntPtr(di2);
if (ptr == IntPtr.Zero)
{
break;
}
string str = Marshal.PtrToStringAnsi(ptr);
if (str == string.Empty)
{
break;
}
di2 = di2 + IntPtr.Size;
}
Try if it works, then I'll explain how...
important you are leaking memory here...
I've found this example in C:
char **dirs = NULL;
afc_read_directory(afc, "/eafaedf", &dirs);
if (!dirs)
afc_read_directory(afc, "/", &dirs);
printf("Directory time.\n");
for (i = 0; dirs[i]; i++) {
printf("/%s\n", dirs[i]);
free(dirs[i]);
}
if (dirs)
free(dirs);
you are responsible for freeing the memory (see the free inside the cycle and the final free?). In this case (and for other methods that return arrays of C-strings you can use afc_dictionary_free. Note that other methods like afc_receive_data that return a single block of memory you can't use it.

Passing a C# string through Pinvoke

C# side:
[DllImport(#"FileGuidUtils.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern long getReparseType([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath);
C/C++ side:
__declspec(dllexport) ReparseType getReparseType(WCHAR *linkPath) {
HANDLE hFile = CreateFile(linkPath, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return NEITHER;
}
REPARSE_DATA_BUFFER *reparseDataBuffer = (REPARSE_DATA_BUFFER *)malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
DWORD dwRetLen = 0;
BOOL bRet = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwRetLen, NULL);
if (bRet == FALSE)
{
free(reparseDataBuffer);
CloseHandle(hFile);
return NEITHER;
}
ULONG reparseType = reparseDataBuffer->ReparseTag;
free(reparseDataBuffer);
CloseHandle(hFile);
if (reparseType == IO_REPARSE_TAG_SYMLINK) {
return SYMLINK;
}
else if (reparseType == IO_REPARSE_TAG_MOUNT_POINT) {
return JUNCTION;
}
return NEITHER;
}
How is linkPath passed? Is it passed as a malloc'd string and I need to free it up on the C/C++ side or will the CLR take care of it for me? If it is getting cleaned up, am I passing it correctly? Could I just pass it as an ordinary C# string without the [MarshalAsAttribute(UnmanagedType.LPWStr)]? Thanks!
C# will make a copy of the C# string for you into unmanaged memory , pass a pointer to it and delete the string once the function call is finished

Categories

Resources