Question

First off, I found the following two similar questions:

Passing Structure to Windows API in python ctypes

ctypes and passing a by reference to a function

The first does not have an accepted answer, and I do not think that I'm doing anything in separate processes. The second simply points out pointer() and byref(), both of which I have tried using to no avail.

Now, on to my question:

I am trying to call the function WERReportCreate with my own pReportInformation (which is a pointer to a struct whose first data value is its own size). This fails in various ways, depending on how I go about it, but I'm not sure how to do it correctly. It is complicated by the fact that one of the requirements is that the structure know it's own size, which I'm not sure how to programatically determine (though if that was the only issue, I think I would have guessed the right value by now). The relevant information from the WER API is shown below:

HRESULT WINAPI WerReportCreate(
  __in      PCWSTR pwzEventType,
  __in      WER_REPORT_TYPE repType,
  __in_opt  PWER_REPORT_INFORMATION pReportInformation,
  __out     HREPORT *phReportHandle
);

(full info at http://msdn.microsoft.com/en-us/library/windows/desktop/bb513625%28v=vs.85%29.aspx)

typedef struct _WER_REPORT_INFORMATION {
  DWORD  dwSize;
  HANDLE hProcess;
  WCHAR  wzConsentKey[64];
  WCHAR  wzFriendlyEventName[128];
  WCHAR  wzApplicationName[128];
  WCHAR  wzApplicationPath[MAX_PATH];
  WCHAR  wzDescription[512];
  HWND   hwndParent;
} WER_REPORT_INFORMATION, *PWER_REPORT_INFORMATION;

(full info at http://msdn.microsoft.com/en-us/library/windows/desktop/bb513637%28v=vs.85%29.aspx)

This is the code that I have tried:

import ctypes
import ctypes.wintypes

class ReportInfo( ctypes.Structure):
    _fields_ = [ ("dwSize", ctypes.wintypes.DWORD),
                 ("hProcess", ctypes.wintypes.HANDLE),
                 ("wzConsentKey", ctypes.wintypes.WCHAR * 64),
                 ("wzFriendlyEventName", ctypes.wintypes.WCHAR * 128),
                 ("wzApplicationName", ctypes.wintypes.WCHAR * 128),
                 ("wzApplicationPath", ctypes.wintypes.WCHAR * ctypes.wintypes.MAX_PATH),
                 ("wzDescription", ctypes.wintypes.WCHAR * 512),
                 ("hwndParent", ctypes.wintypes.HWND) ]

def genReportInfo():
    import os
    size = 32 #Complete SWAG, have tried many values
    process = os.getpid()
    parentwindow = ctypes.windll.user32.GetParent(process)
    werreportinfopointer = ctypes.POINTER(ReportInfo)
    p_werinfo = werreportinfopointer()
    p_werinfo = ReportInfo(size, process, "consentkey", "friendlyeventname", "appname", "apppath", "desc", parentwindow)
    return p_werinfo

if __name__ == '__main__':
    reporthandle = ctypes.wintypes.HANDLE()
    res = ctypes.wintypes.HRESULT()

            ### First pass  NULL in as optional parameter to get default behavior ###
    p_werinfo = None
    res = ctypes.windll.wer.WerReportCreate(u'pwzEventType', 2, p_werinfo, ctypes.byref(reporthandle))
    print "Return Code",res,"\nHandle",reporthandle #Return Code 0, reporthandle is correct (verified by submitting report in a different test)

    p_werinfo = genReportInfo() # Create our own struct

            ### Try Again Using Our Own Struct (via 'byref')  ###      
    res = ctypes.windll.wer.WerReportCreate(u'pwzEventType', 2, ctypes.byref(p_werinfo), ctypes.byref(reporthandle))
    print "Return Code",res,"\nHandle",reporthandle #Return Code Nonzero, reporthandle is None

            ### Try Again Using Our Own Struct (directly)  ###
    res = ctypes.windll.wer.WerReportCreate(u'pwzEventType', 2, p_werinfo, ctypes.byref(reporthandle))
    print "Return Code",res,"\nHandle",reporthandle #Exception Occurs, Execution halts  

And this is the output I get:

Return Code 0
Handle c_void_p(26085328)
Return Code -2147024809
Handle c_void_p(None)
Traceback (most recent call last):
  File "test.py", line 40, in <module>
    res = ctypes.windll.wer.WerReportCreate(u'pwzEventType', s.byref(reporthandle))
WindowsError: exception: access violation writing 0x0000087C

The fact that it works when I pass in a null, but not when I actually pass my (reference to my?) structure suggests to me I have one of three problems: I am not creating the structure correctly (I'm not certain the wzConsentKey is correctly defined), or I am not correctly figuring out the struct's size (I'm actually using struct.calcsize with various options to get initial guesses, and adding and subtracting 1 randomly), or I am not correctly passing the (reference to the?) structure.

Here is where I've hit a deadend. Any help would be appreciated (as well as suggestions for how to improve the clarity, formatting, or quality of my question; this is my first post).

Was it helpful?

Solution

The general short answer to the posted question is: Be sure you are putting the correct information into the structure, other than that, the provided code is a good example of creating and passing a structure. Here's what solved my problem specifically:

There were two issues with the provided code: First, as Mark Tolonen pointed out, I was passing an incorrect size. Using ctypes.sizeof(ReportInfo) solved that problem. The second issue was that I was using a process ID where a process handle was required. Using OpenProcess to obtain a valid process handle in place of my "process" argument solved the second problem.

To anyone debugging similar issues in the future, printing the HRESULTS out as hex numbers rather than integers to make better sense of the return codes:

print "Return Code %08x" % (res & 0xffffffff)

This, in my case, produced the following results:

Return Code 80070057

for my original error, and

Return Code 80070006

for the second error. Using the information at http://msdn.microsoft.com/en-us/library/bb446131.aspx , I saw the first half was metadata, the second half was my actual error code. After converting the Error Code part of the Hex number back to decimal, I used http://msdn.microsoft.com/en-us/library/bb202810.aspx to determine that

Error Code 87 (57 in hex) meant "Parameter Incorrect" (size was wrong) and

Error Code 6 (6 in hex) meant "The Handle is Invalid" (I was passing in a process ID).

OTHER TIPS

You can use ctypes.sizeof(ReportInfo) to obtains the size in bytes of the structure.

Simply create the ReportInfo instance with genReportInfo. You don't need a pointer at this point:

def genReportInfo():
    import os
    size = ctypes.sizeof(ReportInfo)
    process = os.getpid()
    parentwindow = ctypes.windll.user32.GetParent(process)
    return ReportInfo(size, process, "consentkey", "friendlyeventname", "appname", "apppath", "desc", parentwindow)

Call WerReportCreate like this. byref passes the pointer to the ReportInfo instance.

werinfo = genReportInfo()
res = ctypes.windll.wer.WerReportCreate(u'pwzEventType', 2, ctypes.byref(werinfo), ctypes.byref(reporthandle))

I think that will work for you. I don't have wer.dll so I can't test it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top