【问题标题】:How to detect programmatically whether code is running in shared DLL or exe?如何以编程方式检测代码是否在共享 DLL 或 exe 中运行?
【发布时间】:2010-12-10 15:33:27
【问题描述】:

A 有一个 C# 类,它简化了全局热键的处理。 该类使用Win32-API函数RegisterHotKey()注册热键。

根据 MSDN,此函数需要一个范围为 0x0000 到 0xBFFF 的 ID 值 从应用程序调用并且 ID 值在 0xC000 到 0xFFFF 的范围内时 从共享 DLL 调用。 GlobalAddAtom()可用于获取运行时的ID 在 DLL 中。

为了对类的用户隐藏这种区别,类本身应该决定哪个 注册热键时要使用 ID 范围。好吧,要做到这一点,班级必须 能够检测其代码是在应用程序中运行还是在 一个共享的 DLL。

但是如何做到这一点呢?执行此操作的最佳 C#/.NET 方法是什么?

【问题讨论】:

    标签: c# .net winapi dll registerhotkey


    【解决方案1】:

    这是你的类 - 你知道你把它放在哪里。

    如果您不共享它,则只需选择一个低于 0xBFFF 的 ID 即可。

    如果您的类属于可以由多个应用程序共享的 DLL...或者可以简单地由您无法控制的代码共享,因此无法为...分类 ID,请使用 GlobalAddAtom()获取 ID(记得在 unregister the hotkey 之后拨打 GlobalDeleteAtom())。


    说明

    可能值得花一点时间思考为什么有两个不同的 ID 范围,以及为什么 API 文档建议使用 GlobalAddAtom() 来获取共享 DLL 的后一个范围内的 ID。让我们从RegisterHotKey()的参数的文档开始:

    id
    [在]指定热键的标识符。如果 hWnd 参数为 NULL,则热键与当前线程相关联,而不是与特定窗口相关联。如果已存在具有相同 hWnd 和 id 参数的热键,请参阅备注了解所采取的操作。

    据此,我们可以推测热键由两个潜在的信息对之一唯一标识:线程或窗口句柄,以及任意 16 位数字。如果您指定一个窗口句柄 (HWND),那么the message 将发送到该窗口;否则,它被发送到线程。

    所以...如果您只为给定窗口注册 一个 热键,则 ID 并不重要1;没有其他人可以为该窗口注册热键,其他窗口的热键事件将发布到这些窗口。同样,如果您只为给定线程注册一个无窗口热键,您将只会收到该热键的消息。 如果您控制了应用程序的所有代码,则可以为热键选择所需的任何 ID,使用您想要分配的任何技术;没有其他人会踩到它们,因为您拥有所有可以能够踩到它们的代码

    但是,如果您正在编写一个可以被其他代码调用的通用例程怎么办?你不能可靠地选择一个常量 ID,因为调用者可能已经在使用该 ID,如果他们也使用相同的窗口或线程,你最终会重新定义他们的热键。或者如果(如您的情况)您不知道在运行时之前将注册多少个热键?

    您需要一种方法来确保您在运行时选择的 ID 不会被其他人使用。这就是 GlobalAddAtom() 发挥作用的地方:您传递一个字符串,然后它为您提供了一个保证与该字符串相对应的 ID,而不会与其他字符串相对应;这对于系统来说实际上是唯一的,除非其他人传递了相同的字符串——你可能会想出一个唯一的字符串;只需使用您的公司名称或社会安全号码,以及为您需要的每个新原子增加的前缀。或者,如果您真的很偏执,请使用 GUID。

    真相背后的真相

    除此之外,让我试着澄清一点困惑:Windows 实际上并不关心调用 RegisterHotKey() 的代码是否在 DLL 中。它不能。考虑以下例程:

    void RegisterSuperHappyFunHotKey(HWND hWnd, int id, 
                                     unsigned int fsModifiers, unsigned int vk)
    {
       RegisterHotKey(hWnd, id, fsModifiers, vk);
    }
    

    此例程什么都不做,只是将其参数转发给 WinAPI 函数,这些函数都不能识别调用模块。如果它存在于 DLL 中,则其行为与编译到应用程序本身中的行为相同。 Windows 没有可靠的方法来判断调用的来源,并且热键本身与窗口和线程(或单独的线程)相关联,其中任何一个都可以由 DLL 中的代码控制。现在当然,您可以有自己的应用程序或库特定要求:如果您的 DLL 创建一个窗口并为其设置一个热键,那么您需要在销毁窗口时取消注册该热键。 .. 但这是您自己的问题,您可以按照自己的意愿处理。

    MSDN 指定这两个 ID 范围有一个很好的理由:鼓励 DLL 作者避免踩踏应用程序作者使用的 ID。如果您是应用程序的作者,那么世界就是您的牡蛎 - 您(在很大程度上)控制在您的应用程序进程中加载​​和执行哪些代码,因此可以决定哪些代码您使用的 ID无论您认为合适:从 0 开始并为每个新热键递增是完全可以接受的。但是,一旦您冒险进入 ID 的上限范围,您将不得不像使用 DLL 一样使用GlobalAddAtom() - 否则您将面临与第三方代码以这种方式生成的 ID 冲突的风险动态链接库。这是...social contract 之类的。

    总结:

    “共享 DLL”位在这里是一个红鲱鱼;如果您知道应用程序注册的所有热键的 ID,则只需选择一个低于 0xBFFF 的数字并使用它。如果你不能,因为你的代码将被多个调用者使用(就像你的......),然后使用GlobalAddAtom() 获取一个 ID 并使用它。

    推荐

    出于这些原因,我建议您这样做在您正在设计的课程中使用GlobalAddAtom(),只是因为这听起来好像您现在不知道您是否'将把它构建到您自己设计的应用程序中(您可以控制正在使用的 ID)或由其他应用程序加载的 DLL(您)。不用担心 - 只要您遵守为 DLL 调用者规定的规则,您假装 成为 DLL 并没有违反合同。


    1好的,所以有几个系统定义的热键 ID 需要注意...

    【讨论】:

    • 额,如果他在打包源码呢?
    • 当然,我知道我在哪里使用它。但是,如果这个类的用户必须处理 ID 并且必须考虑 ID 的范围,那么对于这样一个类来说,这将不是一个好的设计。所以我想在课堂上隐藏这个。可重用性是这里的另一点。
    • 好的,我试着解释得更好一点。基本上,要么你可以可靠地选择 ID,并且知道它,要么你不能知道你不能——在前一种情况下,使用低范围;在后者中,使用GlobalAddAtom()。从来没有一种情况可以让您决定在运行时使用哪个 ID 范围;如果您无法提前决定,您将始终使用高范围(和 GlobalAddAtom())。
    • @Shog9:感谢您提供详细(和良好)的解释。你说:“基本上,要么你可以可靠地选择 ID 并且知道它,要么你不能并且知道你不能”。但这是错误的。该类在内部处理 ID。如果有人在应用程序中使用此类,则该类必须使用 ID
    • 您可以在您的应用程序中使用GlobalAddAtom()。您只是没有必须 - 如果它是您的应用程序,您知道低范围内的哪些 ID 可用,并且可以简单地从中选择。 MSDN 所说的本质上是“如果您不是应用程序,请不要踩踏为应用程序保留的 ID”,并建议 GlobalAddAtom() 作为选择不会在其他地方使用的 ID 的可靠方法。如果您不能保证您的代码不会被使用其他方式选择 ID 的应用程序使用,请使用 GlobalAddAtom() 选择它们。
    【解决方案2】:

    完成菲利普的回答:

    你需要获取调用你的函数的程序集的引用,所以代码应该是这样的:

    Assembly assembly = Assembly.GetCallingAssembly();
    Boolean isDll = assembly.EntryPoint == null;
    

    希望这会有所帮助。

    里卡多·拉塞尔达·布兰科堡

    【讨论】:

    • 如果此代码位于 dll 内的公共方法中,但从可执行文件中调用 - 第一行将返回什么?
    【解决方案3】:

    试试这个:

    bool isDll = this.GetType().Assembly.EntryPoint == null;
    

    MSDN:

    Assembly.EntryPoint Property

    "属性值一个 MethodInfo 对象 表示入口点 这个大会。如果没有入口点 找到(例如,程序集是 DLL),一个空引用(Nothing in Visual Basic) 被返回。 "

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多