为 C# P/Invoke 调用 C++ 获取正确参数时出现问题动态链接库

发布于 2024-08-25 16:50:27 字数 2276 浏览 1 评论 0 原文

尝试从 Oracle 的 Outside In API 互操作功能。 具有以下功能:

SCCERR EXOpenExport(VTHDOC hDoc, VTDWORD dwOutputId, VTDWORD dwSpecType, 
                    VTLPVOID pSpec, VTDWORD dwFlags, VTSYSPARAM dwReserved,
                    VTLPVOID pCallbackFunc, VTSYSPARAM dwCallbackData, VTLPHEXPORT phExport);

从头文件中我将参数减少为:

#define VTVOID void
typedef VTSYSPARAM VTHDOC, VTLPHDOC *;
typedef DWORD_PTR VTSYSPARAM;
typedef unsigned long DWORD_PTR;
typedef unsigned long VTDWORD;
typedef VTVOID* VTLPVOID;
typedef VTHDOC VTHEXPORT, *VTLPEXPORT;

这些用于 32 位窗口。

浏览头文件、示例程序和文档,我发现:

  1. pSpec 可能是指向缓冲区或 NULL 的指针,因此我将其设置为 >IntPtr.Zero(文档)。
  2. 根据文档“必须由开发人员将其设置为 0”,dwFlagsdwReserved
  3. 如果我不想处理回调,则可以将 pCallbackFunc 设置为 NULL。
  4. 最后两个基于我编写的使用 [StructLayout(LayoutKind.Sequential)] 的 C# 包装器的结构。然后实例化并生成参数,首先使用 Marshal.AllocHGlobal(Marshal.SizeOf(instance)) 创建一个 IntPtr,然后获取作为传递的地址值uint 用于 dwCallbackDataIntPtr 用于 phExport

最终的参数列表如下:

  1. phDoc 作为一个 IntPtr,由之前调用的 DAOpenDocument 函数加载了地址。
  2. dwOutputId 作为 uint 设置为 1535,表示 FI_JPEGFIF
  3. dwSpecType 作为 int 设置为 2,表示 IOTYPE_ANSIPATH
  4. pSpec 作为 IntPtr.Zero,输出将写入其中
  5. dwFlags as uint 设置为 0 按照指示
  6. dwReserved as uint 设置为 >0 按照指示将
  7. pCallbackFunc 作为 IntPtr 设置为 NULL,因为我会将结果
  8. dwCallBackData 处理为 < code>uint struct phExport 的缓冲区地址
  9. 作为 IntPtr 到另一个 struct buffer

仍然从 API 收到未定义的错误。这意味着调用返回一个未在任何头文件中定义的 961。过去,当我选择的参数类型不正确时,我就会遇到这种情况。

我开始使用 Interop Assistant,它有助于了解有多少参数类型被翻译。然而,它受到我从头文件中收集正确的本机类型的能力的限制。例如,前面函数中使用的 hDoc 参数被定义为非文件系统句柄,因此尝试使用 Marshal 创建句柄,然后使用 IntPtr ,最后结果是一个int(实际上是这里用的&phDoc)。

那么,除了反复试验之外,还有更科学的方法吗?

吉姆

Trying to Interop a functionality from the Outside In API from Oracle.
Have the following function:

SCCERR EXOpenExport(VTHDOC hDoc, VTDWORD dwOutputId, VTDWORD dwSpecType, 
                    VTLPVOID pSpec, VTDWORD dwFlags, VTSYSPARAM dwReserved,
                    VTLPVOID pCallbackFunc, VTSYSPARAM dwCallbackData, VTLPHEXPORT phExport);

From the header files I reduced the parameters to:

#define VTVOID void
typedef VTSYSPARAM VTHDOC, VTLPHDOC *;
typedef DWORD_PTR VTSYSPARAM;
typedef unsigned long DWORD_PTR;
typedef unsigned long VTDWORD;
typedef VTVOID* VTLPVOID;
typedef VTHDOC VTHEXPORT, *VTLPEXPORT;

These are for 32 bit windows.

Going through the header files, the example programs, and the documentation I found:

  1. That pSpec could be a pointer to a buffer or NULL, so I set it to a IntPtr.Zero (documentation).
  2. That dwFlags and dwReserved according to the documentation "Must be set by the developer to 0".
  3. That pCallbackFunc can be set to NULL if I don't want to handle callbacks.
  4. That the last two are based on structs that I wrote C# wrappers for using the [StructLayout(LayoutKind.Sequential)]. Then instatiated an instance and generated the parameters by first creating an IntPtr with Marshal.AllocHGlobal(Marshal.SizeOf(instance)), then getting the address value which is passed as a uint for dwCallbackData and a IntPtr for phExport.

The final parameter list is as follows:

  1. phDoc as an IntPtr which was loaded with an address by the DAOpenDocument function called before.
  2. dwOutputId as uint set to 1535 which represents FI_JPEGFIF
  3. dwSpecType as int set to 2 which represents IOTYPE_ANSIPATH
  4. pSpec as an IntPtr.Zero where the output will be written
  5. dwFlags as uint set to 0 as directed
  6. dwReserved as uint set to 0 as directed
  7. pCallbackFunc as IntPtr set to NULL as I will handle results
  8. dwCallBackData as uint the address of a buffer for a struct
  9. phExport as IntPtr to another struct buffer

Still get an undefined error from the API. Meaning that the call returns a 961 which is not defined in any of the header files. In the past I have gotten this when my choice of parameter types are incorrect.

I started out using Interop Assistant which was helpful in learning how many of the parameter types get translated. It is however limited by how well I am able to glean the correct native type from the header files. For example the hDoc parameter used in the preceding function was defined as a non-filesytem handle, so attempted to use Marshal to create a handle, then used an IntPtr, and finally it turned out to be an int (actually it was &phDoc used here).

So is there a more scientific way of doing this, other than trial and error?

Jim

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爺獨霸怡葒院 2024-09-01 16:50:27

您的定义:

typedef unsigned long DWORD_PTR;

不正确 - DWORD_PTR 定义为:

typedef ULONG_PTR DWORD_PTR;

反过来又定义为:

typedef unsigned __int3264 ULONG_PTR;

属性 __int3264 记录在 这里,我复制了最重要的一点:

关键字__int3264指定具有以下属性的整型:

  • 在 32 位平台上是 32 位
  • 在 64 位平台上是 64 位
  • 它是 32 位的,以实现向后兼容性。它在发送端被截断,并在接收端被适当扩展(有符号或无符号)。

当您编组 C“类型”(例如 DWORD_PTR)时,其大小取决于平台指针大小,这通常是操作系统参数的情况(例如 LPARAM WPARAMLRESULTSIZE_T 等)(名称 VTSYSPARAM 似乎表明了这一点),您应该使用 IntPtr code>,而不是 uint

基本上:

  • dwReserved as uint 按照指示设置为 0
  • dwCallBackData 作为 uint 结构体缓冲区的地址
  • 应该是:

  • dwReserved as IntPtr 按照指示设置为 IntPtr.Zero
  • dwCallBackData 作为 IntPtr 结构体缓冲区的地址
  • 然后它应该可以工作,如果没有,您需要确保在以下情况下没有犯类似的错误整理必要的结构。

    Your definition:

    typedef unsigned long DWORD_PTR;
    

    Is incorrect — DWORD_PTR is defined as:

    typedef ULONG_PTR DWORD_PTR;
    

    Which in turn is defined as:

    typedef unsigned __int3264 ULONG_PTR;
    

    The attribute __int3264 is documented here, I am reproducing the most important bit:

    The keyword __int3264 specifies an integral type that has the following properties:

    • It is 32-bit on 32-bit platforms
    • It is 64-bit on 64-bit platforms
    • It is 32-bit on the wire for backward compatibility. It gets truncated on the sending side and extended appropriately (signed or unsigned) on the receiving side.

    When you are marshaling C "types" such as DWORD_PTR whose size depends on the platform pointer size which is often the case with the OS parameters (such as LPARAM, WPARAM, LRESULT, SIZE_T, etc) which the name VTSYSPARAM appears to suggest, you should use IntPtr, not uint.

    Basically:

    1. dwReserved as uint set to 0 as directed
    1. dwCallBackData as uint the address of a buffer for a struct

    Should be:

    1. dwReserved as IntPtr set to IntPtr.Zero as directed
    1. dwCallBackData as IntPtr the address of a buffer for a struct

    Then it should work, and if it doesn't you need to make sure you didn't make a similar mistake when marshaling the necessary structures.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文