我用 C# 编写了一个库(运行良好)来运行 Win32 函数。例如 PInvoking CredUIPromptForWindowsCredentials、CredUnPackAuthenticationBuffer、CredPackAuthenticationBuffer,因此一个方法调用可以返回 NetworkCredentials,而无需您自己进行所有凭据打包/解包和错误检查。以及类似的类和方法,用于使用 NetUser... 和 NetGroup... 函数从计算机创建/列出/更改/删除本地用户和本地组
//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;
}
这一切都适用于调用它们的任何其他 C# 库或应用程序。 现在我正在用 C 语言编写几个应用程序,它们使用的功能与我刚刚为 C# 库创建的功能相同,所以为什么不在 C 语言中将其用于这些 C 应用程序,让编写应用程序本身更简单。
//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);
这对我用 C 编写的应用程序来说非常有用。但是我遇到的麻烦是当我决定尝试调用我从 C# 编写的这些 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);
...
}
在调试 C# 代码时,它成功地进入了我导出的 C dll 函数,它在调用
NetGroupEnum
时崩溃并出现访问冲突错误(在文档中说系统分配了一个缓冲区,而不是调用者)相同当 C# 应用程序调用我的 C 函数调用 CredUIPromptForWindowsCredentials
(这也表示系统分配缓冲区)时,错误导致 C# 应用程序崩溃。
并不是 Win32 函数返回然后我在访问它给我的缓冲区时出错,而是调试器给我一个错误框而没有从该函数返回。
对于 AccGroupExists,它使用 NetUserGetInfo
函数,并且只获取用户名,返回而不会崩溃,这个函数填充你给它的缓冲区,而不是“系统分配的缓冲区”。
我已经尝试更改我导出的 C 函数以具有不同的参数类型,并在调用 Win32 函数之前分配 C 函数内的任何字符串并将传递的 C# 字符串复制到 wchar_t* 中。我尝试更改 PInvoke 参数类型。
所以看来我必须满足于为 C# 做 C#->Win32,为 C 做 C->Win32。但我不能使用 C#->C->Win32。
我可以在 C# 中做些什么,比如在调用我的 C dll 之前设置内存安全或进程访问权限,这将允许 Win32 函数分配内存而不会使应用程序崩溃?
编辑
这是 C 库中的一个函数,在另一个 C 应用程序中调用时它对我来说工作正常。但是在 C# 的
NetLocalGroupEnum
处失败。
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;
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;}