我有一个现有的应用程序P/Invokes到一个DLL与应用程序本身驻留在同一目录中.
现在(由于佳能生产了最疯狂的API之一)我需要支持这个API的两个版本,并在运行时确定我应该使用哪个(旧的或新的).由于DLL具有相同的名称(第一个加载具有相同名称的其他DLL,因此只重命名第一个将无法帮助我)我必须将它们保存在不同的目录中.
因此我的问题是:我有什么选择来控制DllImport声明中给出的DLL使用的目录?
我想我可以尝试以下两个想法中的任何一个:
1)在执行第一个P/Invoke之前使用"SetDllDirectory"设置我想要的目录,然后重置它.
2)使用"LoadLibraryEx"手动加载所需的DLL,并希望这样做.
但是,有没有更多的".NET:ish方式"首先尝试?
更新:我意识到我可以在两个单独的.Net程序集中填充对DLL的所有访问权限,然后将它们中的每一个放在一个带有相应API文件的单独目录中.然后我可以动态加载正确的.Net程序集,并自动加载正确的DLL.有什么理由不行吗?
我能想到一个:我将如何调试这些东西?有可能告诉Visual Studio一个程序集(包含在我的解决方案中)应该放在一个子目录中并从那里进行调试吗?
我已经开始将.NET 2.0 WinForms应用程序升级到.NET 4.0.嗯,好的,升级过程只是切换平台目标,但让它真正起作用.我认为这就是它的全部内容.
但似乎.NET 4.0中关于互操作的东西发生了巨大的变化.使用DllImport(),该应用程序嵌入了几个Delphi dll.当应用程序面向.NET 2.0时,一切正常.但是当我将它改为目标.NET 4.0时,东西开始变得混乱,就像破坏内存的东西一样.
例如,它在奇怪的地方用"0"替换单个数字.在IStream中传递的数据用(Hex)00 00 00 00 00 00 00 80替换8个字符,但只有大约70%的时间.两次连续调用以检索相同的值会返回不同的结果(从内存中的缓存中检索值,第一次成功,第二次失败).发送到日志的字符串显示为截断.
我尝试过很多尝试使调用约定更加明确的东西,没有任何效果.所有字符串在.NET端处理为[MarshalAs(UnmanagedType.LPWStr)],在Delphi端处理为PWChar.
在.NET 4.0中改变了什么会破坏P/Invoke?
- - - - - - - - - - - - - - 编辑 - - - - - - - - - - - ----------------
这是最简单的例子.它生成一个PDF,有时可以正常工作,但更频繁地最终损坏(并在.NET 2.0中正常工作):
[DllImport(DLLName)]
public static extern void SetDBParameters(
[MarshalAs(UnmanagedType.LPWStr)] string Server,
[MarshalAs(UnmanagedType.LPWStr)] string Database,
[MarshalAs(UnmanagedType.LPWStr)] string User,
[MarshalAs(UnmanagedType.LPWStr)] string Password,
short IntegratedSecurity);
procedure SetDBParameters(Server, Database, User, Password: PWChar;
IntegratedSecurity: WordBool); stdcall;
[DllImport(DLLName)]
public …Run Code Online (Sandbox Code Playgroud) private const int TheAnswer = 42;
Run Code Online (Sandbox Code Playgroud)
但有时我们需要从Windows API中表示已存在的常量.
例如,我不知道如何命名:
/// <summary>
/// With this style turned on for your form,
/// Windows double-buffers the form and all its child controls.
/// </summary>
public const int WS_EX_COMPOSITED = 0x02000000;
Run Code Online (Sandbox Code Playgroud)
我该怎么命名呢?
保持它WS_EX_COMPOSITED可以让我快速将它与WinAPI相关联,但这是错误的.
一些选择:
WsExComposited - 太匈牙利人了Composited - 太短WsExenum with Compositedin it - 仍然是匈牙利人ExtendedWindowsStyles.Composited - 班级常数?枚举?应该指出的是,良好命名的目标是:
如何struct使用pinvoke 处理从C#调用的dll方法中的可选参数?例如,lpSecurityAttributes此处的参数应该null在不存在时传递.
正确的传递方式struct似乎正在使用ref,但它不能有可选参数,或者null总的来说.
有什么方法可以达到这个目的?
我在C#中创建一个小实用程序,当按下全局热键时,它会向活动文本框添加一些文本,这是一种自动完成功能.我有我的全局热键工作,但现在我不知道如何在活动文本框中获取当前文本(如果活动窗口是一个文本框).到目前为止我尝试过的是使用
一个.GetForegroundWindow然后使用该句柄调用GetWindowText.这给了我活动窗口的窗口标题,而不是文本框内容.
湾 GetActiveWindow并使用该句柄调用GetWindowText.这根本不给我任何文字.
这是我所做的一个例子
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[ DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[ DllImport("user32.dll") ]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);
[DllImport("user32.dll")]
static extern int GetActiveWindow();
public static void TestA() {
int h = GetForegroundWindow();
StringBuilder b = new StringBuilder();
GetWindowText(h, b, 256);
MessageBox.Show(b.ToString());
}
public static void TestB() {
int h = GetActiveWindow();
StringBuilder b = new StringBuilder();
GetWindowText(h, b, 256);
MessageBox.Show(b.ToString());
}
Run Code Online (Sandbox Code Playgroud)
那么,关于如何实现这一点的任何想法?
编辑28.01.2009: 所以,我发现了如何做到这一点.这是我用过的:
using …Run Code Online (Sandbox Code Playgroud) 在学习P/Invoke的过程中,我问过上一个问题:
但是,我不太明白在C#中使用P/Invoke而不是在托管C++中创建包装器的含义.在C#中使用P/Invoke创建相同的DLL肯定会导致更清晰的接口,因为我可以在嵌入式资源上使用DLLImport,但是我自己编组的本机DLL的托管C++包装器会有更好的性能吗?
我在.NET中重构一个庞大而复杂的代码库,大量使用P/Invoke到Win32 API.项目的结构并不是最好的,我发现DllImport语句遍布整个地方,经常复制到同一个函数,并且还以各种方式声明:
import指令和方法有时被声明为public,有时是private,有时是静态的,有时也是实例方法.我担心重构可能会产生意想不到的后果,但这可能是不可避免的.
是否有可以帮助我的最佳实践记录?
我的instict是组织一个静态/共享Win32 P/Invoke API类,它在一个文件中列出所有这些方法和相关常量... 编辑有超过70个user32 DLL导入.
(代码库由20多个项目组成,包含大量的窗口消息传递和跨线程调用.如果有所不同,它也是从VB6升级的VB.NET项目.)
在找到C#和C++之间的互操作方法的过程中,我发现这篇文章解释了P/Invoke.
我读了很多文章声称C++/CLI不是精确的C++,需要一些努力来修改原始的C++代码.
我想问一下,当我想要从C#对象中使用一些C++对象(代码/数据)时,最佳方法是什么.
我正在使用C#项目DeviceIoControl.我已经为我的签名咨询了相关的Pinvoke.net页面:
[DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
EIOControlCode IoControlCode,
[MarshalAs(UnmanagedType.AsAny)]
[In] object InBuffer,
uint nInBufferSize,
[MarshalAs(UnmanagedType.AsAny)]
[Out] object OutBuffer,
uint nOutBufferSize,
out uint pBytesReturned,
[In] IntPtr Overlapped
);
Run Code Online (Sandbox Code Playgroud)
我从来没有见过object和前,但MSDN文档听起来前途:[MarshalAs(UnmanagedType.AsAny)]
一种动态类型,它在运行时确定对象的类型,并将对象编组为该类型.该成员仅对平台调用方法有效.
我的问题是:使用此签名的"最佳"和/或"正确"方式是什么?
例如,IOCTL_STORAGE_QUERY_PROPERTY期望InBuffer是一个STORAGE_PROPERTY_QUERY结构.看起来我应该能够定义该结构,创建一个new实例,并将其传递给我的Pinvoke签名:
var query = new STORAGE_PROPERTY_QUERY { PropertyId = 0, QueryType = 0 };
DeviceIoControl(..., query, Marshal.SizeOf(query), ...);
Run Code Online (Sandbox Code Playgroud)
但是,我刚刚System.ExecutionEngineException做了这个,所以我改为:
int …Run Code Online (Sandbox Code Playgroud) 我正在尝试基于Window句柄捕获C#中的桌面窗口.我正在使用.NET并使用PInvoke来GetWindowRect()来捕获窗口矩形.我有窗口选择和矩形捕获工作正常.
但是,捕获的窗口矩形不仅包括实际窗口大小,还包括Window的装饰,如周围的阴影.当我尝试将窗口剪辑为位图时,位图包含区域和阴影.在Windows 10上,我得到透明阴影区域,包括活动窗口下方可能显示的任何内容:
我使用的代码很简单,通过PInvoke调用使用Win32 GetWindowRect()捕获Window:
var rect = new Rect();
GetWindowRect(handle, ref rect);
var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
var result = new Bitmap(bounds.Width, bounds.Height);
using (var graphics = Graphics.FromImage(result))
{
graphics.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}
return result;
Run Code Online (Sandbox Code Playgroud)
然后我捕获图像并将其分配到图片框中.
此外,它看起来像窗户之间有一些变化 - 有些窗户有阴影,有些窗户没有.大多数人都这样做,但是像Visual Studio和Chrome这样的人没有,所以它甚至不是一个简单的问题,即剥离无关的像素.
我已经尝试过使用GetClientRect(),但这只是客户区,这不是我所追求的.我想得到的是带有边框但没有阴影的实际窗口矩形.
反正有没有这样做?