I had written a library (which worked fine) in C# for running Win32 functions. Such as PInvoking CredUIPromptForWindowsCredentials,CredUnPackAuthenticationBuffer,CredPackAuthenticationBuffer, so one method call can return NetworkCredentials without you doing all the credential packing/unpacking and error checking youself. And similarly classes and methods for creating/listing/changing/removing local users and local groups from a computer using NetUser... and NetGroup... functions
//ToolsCS.cs
public static class Credentials{
public static NetworkCredential GetCredentials(string promtpCaption, string promptMessage, string inUsername, string inDomain, string inPassword){...}
public static NetworkCredential GetCredentials(string promtpCaption, string promptMessage){...}
}
public class User{
public static User[] GetUsers(string computername){...}
public static bool Exists(string computername,string username){...}
public static void Create(string computername,User user){...}
public static void Delete(string computername,string username){...}
public static void Set(string computername,string username,User user){...}
public string Name;
public string DisplayName;
public string Description;
}
This all works great for any other C# library or app that calls them. Now I am writing a couple apps in C, which are using the same functions i just made my C# library for, so why not make it in C to use for these C apps, make writing the app itself simpler.
//ToolsC.c
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupList(wchar_t* system,wchar_t** names,DWORD* count);
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupExists(wchar_t* system,wchar_t* name);
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupCreate(wchar_t* system,wchar_t* name);
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupSet(wchar_t* system,wchar_t* name);
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupRemove(wchar_t* system,wchar_t* name);
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupAddMember(wchar_t* system,wchar_t* group,wchar_t* member);
This all workds great for the apps im writing in C. But the trouble I get is when I decided to try calling these C functions i wrote from C#
//SomeApp.cs
class SomeClass{
[DllImport(@"ToolsC.dll",SetLastError=true)]
public static extern bool AccGroupList(
[MarshalAs(UnmanagedType.LPWStr)] string system,
[MarshalAs(UnmanagedType.LPArray,SizeParamIndex=2)] ref string[] names,
ref int count);
...
}
When debugging the C# code, it successfully steps into my exported C dll function it crashes with a Access Violation error when it makes the call to NetGroupEnum
(which in documentation says that the system allocates a buffer, not the caller) the same error crashes the C# app when it calls my C function the calls CredUIPromptForWindowsCredentials
(which also says the system allocates a buffer).
It's not that the Win32 functions return and then I get error when accessing the buffer it gave me, It's that the debugger gives me an error box without returning from that function.
For AccGroupExists, it uses NetUserGetInfo
function, and only gets the user's name, returns wihtout crashing, this function fills a buffer you give it, not a "system allocated buffer".
I already tried changing the my exported C functions to have different parameter types, and allocating any strings inside the C function and copying the passed C# strings into the wchar_t* before calling the Win32 functions. And I tried changing the PInvoke parameter types.
So it seems like I have to settle for doing C#->Win32 for C#, and C->Win32 for C. But I cant use C#->C->Win32.
Is there something I can do in C# like setting the memory security or process access rights before I make calls to my C dll that will allow the Win32 functions to allocate memory without crashing the application?
EDIT
Here is a function in the C library, which works fine for me when called in another C app. But fails at NetLocalGroupEnum
from C#.
extern "C" __declspec(dllexport) BOOL __stdcall AccGroupList(wchar_t* system,wchar_t** names,DWORD* count){
GROUP_INFO_0* buffer;
DWORD readcount=0;
DWORD totalcount=0;
DWORD size=0;
//C# app crashes here
error=NetLocalGroupEnum(system,0,(LPBYTE*)&buffer,MAX_PREFERRED_LENGTH,&readcount,&totalcount,NULL);
if(error!=NERR_Success){
SetLastError(error);
return FALSE;}
if((names=(wchar_t**)CoTaskMemAlloc(sizeof(wchar_t*)*readcount))==NULL){
NetApiBufferFree(&buffer);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;}
for(int i=0;i<readcount;i++){
size=wcslen(buffer[i].grpi0_name);
if((names[i]=(wchar_t*)CoTaskMemAlloc(2*size))==NULL){
for(int j=0;j<i;i++){
size=wcslen(names[j]);
wmemset(names[j],0,size);
CoTaskMemFree(names[j]);}
NetApiBufferFree(&buffer);
CoTaskMemFree(names);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;}
wmemset(names[i],0,size);
wcscpy(names[i],buffer[i].grpi0_name);}
NetApiBufferFree(&buffer);
*count=readcount;
return TRUE;}