Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
June 2018
- 68 participants
- 1149 messages
Re: [PATCH 1/2] schedsvc: Load job files at the service start up.
by Dmitry Timoshkov
There is a problem with this patchset, please ignore it for now.
--
Dmitry.
June 7, 2018
Re: [PATCH 1/4 v5] riched20/tests: Rewrite tests for ITextServices_TxGetNaturalSize().
by Huw Davies
On Wed, Jun 06, 2018 at 05:24:13PM +0800, Jactry Zeng wrote:
> Superseded patch 146570.
>
> ChangeLog:
> v5:
> - Use DrawTextW for calculating expected values;
> - Simplify format setting with EM_SETCHARFORMAT.
>
> Signed-off-by: Jactry Zeng <jzeng(a)codeweavers.com>
> ---
> dlls/riched20/tests/txtsrv.c | 125 +++++++++++++++++------------------
> 1 file changed, 61 insertions(+), 64 deletions(-)
>
> diff --git a/dlls/riched20/tests/txtsrv.c b/dlls/riched20/tests/txtsrv.c
> index 88b5adf13c..6e61392dc5 100644
> --- a/dlls/riched20/tests/txtsrv.c
> +++ b/dlls/riched20/tests/txtsrv.c
> @@ -697,81 +697,78 @@ static void test_TxSetText(void)
> ITextHost_Release(host);
> }
>
> +#define CHECK_TXGETNATURALSIZE(res,width,height,hdc,string) \
> + _check_txgetnaturalsize(res, width, height, hdc, string, __LINE__)
> +static void _check_txgetnaturalsize(HRESULT res, LONG width, LONG height, HDC hdc, LPCWSTR string, int line)
> +{
> + RECT rect;
> + LONG expected_width, expected_height;
> +
> + GetClientRect(WindowFromDC(hdc), &rect);
> + DrawTextW(hdc, string, -1, &rect, DT_LEFT | DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL | DT_WORDBREAK);
Why can't you use GetTextExtentPointW() here? This would avoid the
GetClientRect() call too.
> + expected_width = rect.right - rect.left;
> + expected_height = rect.bottom - rect.top;
> + ok_(__FILE__,line)(res == S_OK, "ITextServices_TxGetNaturalSize failed: 0x%08x.\n", res);
> + ok_(__FILE__,line)(width >= expected_width && width <= expected_width + 1,
> + "got wrong width: %d, expected: %d {+1}.\n", width, expected_width);
> + ok_(__FILE__,line)(height == expected_height, "got wrong height: %d, expected: %d.\n",
> + height, expected_height);
> +}
> +
> static void test_TxGetNaturalSize(void)
> {
> ITextServices *txtserv;
> ITextHost *host;
> HRESULT result;
> - BOOL ret;
> -
> - /* This value is used when calling TxGetNaturalSize. MSDN says
> - that this is not supported however a null pointer cannot be
> - used as it will cause a segmentation violation. The values in
> - the structure being pointed to are required to be INT_MAX
> - otherwise calculations can give wrong values. */
> - const SIZEL psizelExtent = {INT_MAX,INT_MAX};
> -
> - static const WCHAR oneA[] = {'A',0};
> -
> - /* Results of measurements */
> - LONG xdim, ydim;
> -
> - /* The device context to do the tests in */
> + SIZEL psizelExtent = {-1,-1};
Let's just call this 'extent';
> + static const WCHAR test_text[] = {'T','e','s','t','S','o','m','e','T','e','x','t',0};
> + LONG width, height;
> HDC hdcDraw;
> -
> - /* Variables with the text metric information */
> - INT charwidth_caps_text[26];
> - TEXTMETRICA tmInfo_text;
> + HWND hwnd;
> + RECT rect;
> + CHARFORMAT2W cf;
> + LRESULT lresult;
> + HFONT hf;
> + LOGFONTW lf;
>
> if (!init_texthost(&txtserv, &host))
> return;
>
> - hdcDraw = GetDC(NULL);
> - SaveDC(hdcDraw);
> -
> - /* Populate the metric strucs */
> - SetMapMode(hdcDraw,MM_TEXT);
> - GetTextMetricsA(hdcDraw, &tmInfo_text);
> - SetLastError(0xdeadbeef);
> - ret = GetCharWidth32A(hdcDraw,'A','Z',charwidth_caps_text);
> - if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
> - win_skip("GetCharWidth32 is not available\n");
> - goto cleanup;
> - }
> -
> - /* Make measurements in MM_TEXT */
> + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
> + 0, 0, 100, 100, 0, 0, 0, NULL);
> + hdcDraw = GetDC(hwnd);
> SetMapMode(hdcDraw,MM_TEXT);
> - xdim = 0; ydim = 0;
> -
> - result = ITextServices_TxSetText(txtserv, oneA);
> - ok(result == S_OK, "ITextServices_TxSetText failed (result = %x)\n", result);
> - if (result != S_OK) {
> - skip("Could not set text\n");
> - goto cleanup;
> - }
> -
> - SetLastError(0xdeadbeef);
> - result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT,
> - hdcDraw, NULL, NULL,
> - TXTNS_FITTOCONTENT, &psizelExtent,
> - &xdim, &ydim);
> - todo_wine ok(result == S_OK || broken(result == E_FAIL), /* WINXP Arabic Language */
> - "TxGetNaturalSize gave unexpected return value (result = %x)\n", result);
> - if (result == S_OK) {
> - todo_wine ok(ydim == tmInfo_text.tmHeight,
> - "Height calculated incorrectly (expected %d, got %d)\n",
> - tmInfo_text.tmHeight, ydim);
> - /* The native DLL adds one pixel extra when calculating widths. */
> - todo_wine ok(xdim >= charwidth_caps_text[0] && xdim <= charwidth_caps_text[0] + 1,
> - "Width calculated incorrectly (expected %d {+1}, got %d)\n",
> - charwidth_caps_text[0], xdim);
> - } else
> - skip("TxGetNaturalSize measurements not performed (xdim = %d, ydim = %d, result = %x, error = %x)\n",
> - xdim, ydim, result, GetLastError());
> -
> -cleanup:
> - RestoreDC(hdcDraw,1);
> - ReleaseDC(NULL,hdcDraw);
> + GetClientRect(hwnd, &rect);
> +
> + memset(&cf, 0, sizeof(cf));
> + cf.cbSize = sizeof(cf);
> + cf.dwMask = CFM_ALL2;
> + hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
No need for cast.
> + GetObjectW(hf, sizeof(LOGFONTW), &lf);
> + lstrcpyW(cf.szFaceName, lf.lfFaceName);
> + cf.yHeight = MulDiv(abs(lf.lfHeight), 1440, GetDeviceCaps(GetDC(NULL), LOGPIXELSY));
> + if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
> + if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
> + if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
> + if (lf.lfStrikeOut) cf.dwEffects |= CFE_SUBSCRIPT;
> + cf.bPitchAndFamily = lf.lfPitchAndFamily;
> + cf.bCharSet = lf.lfCharSet;
> + result = ITextServices_TxSendMessage(txtserv, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf, &lresult);
> + ok(result == S_OK, "ITextServices_TxSendMessage failed: 0x%08x.\n", result);
> + SelectObject(hdcDraw, hf);
> +
> + result = ITextServices_TxSetText(txtserv, test_text);
> + ok(result == S_OK, "ITextServices_TxSetText failed: 0x%08x.\n", result);
> +
> + psizelExtent.cx = -1; psizelExtent.cy = -1;
This is already initialised.
> + width = rect.right - rect.left;
> + height = 0;
> + result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT, hdcDraw, NULL, NULL,
> + TXTNS_FITTOCONTENT, &psizelExtent, &width, &height);
> + todo_wine CHECK_TXGETNATURALSIZE(result, width, height, hdcDraw, test_text);
> +
> + ReleaseDC(hwnd, hdcDraw);
> + DestroyWindow(hwnd);
> ITextServices_Release(txtserv);
> ITextHost_Release(host);
> }
> --
> 2.17.1
>
>
>
>
June 7, 2018
Re: [PATCH 4/4] wsdapi: Add initial support for reading messages; identify Probe message.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:52PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/soap.c | 340
> +++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/tests/discovery.c | 6 +-
> 2 files changed, 341 insertions(+), 5 deletions(-)
>
>
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index 4e191a5c82..2bc974f111 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -45,6 +45,13 @@ static const WCHAR actionHello[] = {
> 'd','i','s','c','o','v','e','r','y','/',
> 'H','e','l','l','o', 0 };
>
> +static const WCHAR actionProbe[] = {
> + 'h','t','t','p',':','/','/',
> + 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
> + 'w','s','/','2','0','0','5','/','0','4','/',
> + 'd','i','s','c','o','v','e','r','y','/',
> + 'P','r','o','b','e', 0 };
> +
> static const WCHAR actionBye[] = {
> 'h','t','t','p',':','/','/',
> 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
> @@ -98,6 +105,27 @@ struct discovered_namespace
> LPCWSTR uri;
> };
>
> +static LPWSTR utf8_to_wide(void *parent, const char *utf8_str, int length)
> +{
> + int utf8_str_len = 0, chars_needed = 0, bytes_needed = 0;
> + LPWSTR new_str = NULL;
> +
> + if (utf8_str == NULL) return NULL;
> +
> + utf8_str_len = (length < 0) ? lstrlenA(utf8_str) : length;
> + chars_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, NULL, 0);
> +
> + if (chars_needed <= 0) return NULL;
> +
> + bytes_needed = sizeof(WCHAR) * (chars_needed + 1);
> + new_str = WSDAllocateLinkedMemory(parent, bytes_needed);
> +
> + MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, new_str, chars_needed);
> + new_str[chars_needed] = 0;
> +
> + return new_str;
> +}
> +
> static char *wide_to_utf8(LPCWSTR wide_string, int *length)
> {
> char *new_string = NULL;
> @@ -1081,8 +1109,316 @@ cleanup:
> return ret;
> }
>
> +static LPWSTR xml_text_to_wide_string(void *parent_memory, WS_XML_TEXT *text)
> +{
> + if (text->textType == WS_XML_TEXT_TYPE_UTF8)
> + {
> + WS_XML_UTF8_TEXT *utf8_text = (WS_XML_UTF8_TEXT *) text;
> + return utf8_to_wide(parent_memory, (const char *) utf8_text->value.bytes, utf8_text->value.length);
> + }
> + else if (text->textType == WS_XML_TEXT_TYPE_UTF16)
> + {
> + WS_XML_UTF16_TEXT *utf_16_text = (WS_XML_UTF16_TEXT *) text;
> + return duplicate_string(parent_memory, (LPCWSTR) utf_16_text->bytes);
> + }
> +
> + FIXME("Support for text type %d not implemented.\n", text->textType);
> + return NULL;
> +}
> +
> +static BOOL move_to_element(WS_XML_READER *reader, const char *element_name, WS_XML_STRING *uri)
return HRESULT
> +{
> + WS_XML_STRING envelope;
> + BOOL found = FALSE;
> +
> + envelope.bytes = (BYTE *) element_name;
> + envelope.length = strlen(element_name);
> + envelope.dictionary = NULL;
> + envelope.id = 0;
> +
> + return SUCCEEDED(WsReadToStartElement(reader, &envelope, uri, &found, NULL)) && found;
> +}
> +
> +static BOOL ws_element_to_wsdxml_element(WS_XML_READER *reader, IWSDXMLContext *context, WSDXML_ELEMENT *parent_element)
and here.
> +{
> + WSDXML_ATTRIBUTE *cur_wsd_attrib = NULL, *new_wsd_attrib = NULL;
> + const WS_XML_ELEMENT_NODE *element_node = NULL;
> + WSDXML_ELEMENT *cur_element = parent_element;
> + const WS_XML_TEXT_NODE *text_node = NULL;
> + LPWSTR uri = NULL, element_name = NULL;
> + WS_XML_STRING *ns_string = NULL;
> + WS_XML_ATTRIBUTE *attrib = NULL;
> + WSDXML_ELEMENT *element = NULL;
> + const WS_XML_NODE *node = NULL;
> + WSDXML_NAME *name = NULL;
> + WSDXML_TEXT *text = NULL;
> + HRESULT ret;
> + int i;
> +
> + for (;;)
> + {
> + if (cur_element == NULL) break;
> +
> + ret = WsReadNode(reader, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + ret = WsGetReaderNode(reader, &node, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + switch (node->nodeType)
> + {
> + case WS_XML_NODE_TYPE_ELEMENT:
> + element_node = (const WS_XML_ELEMENT_NODE *) node;
> +
> + uri = utf8_to_wide(NULL, (const char *) element_node->ns->bytes, element_node->ns->length);
> + if (uri == NULL) goto cleanup;
> +
> + /* Link element_name to uri so they will be freed at the same time */
> + element_name = utf8_to_wide(uri, (const char *) element_node->localName->bytes,
> + element_node->localName->length);
> + if (element_name == NULL) goto cleanup;
> +
> + if (FAILED(IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name))) goto cleanup;
> +
> + WSDFreeLinkedMemory(uri);
> + uri = NULL;
> +
> + if (FAILED(WSDXMLBuildAnyForSingleElement(name, NULL, &element))) goto cleanup;
> + WSDXMLAddChild(cur_element, element);
> +
> + cur_wsd_attrib = NULL;
> +
> + /* Add attributes */
> + for (i = 0; i < element_node->attributeCount; i++)
> + {
> + attrib = element_node->attributes[i];
> + if (attrib->isXmlNs) continue;
> +
> + new_wsd_attrib = WSDAllocateLinkedMemory(element, sizeof(WSDXML_ATTRIBUTE));
> + if (new_wsd_attrib == NULL) goto cleanup;
> +
> + ns_string = attrib->ns;
> + if (ns_string->length == 0) ns_string = element_node->ns;
> +
> + uri = utf8_to_wide(NULL, (const char *) ns_string->bytes, ns_string->length);
> + if (uri == NULL) goto cleanup;
> +
> + /* Link element_name to uri so they will be freed at the same time */
> + element_name = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
> + if (element_name == NULL) goto cleanup;
> +
> + if (FAILED(IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name))) goto cleanup;
> +
> + WSDFreeLinkedMemory(uri);
> + uri = NULL;
> +
> + new_wsd_attrib->Value = xml_text_to_wide_string(new_wsd_attrib, attrib->value);
> + if (new_wsd_attrib->Value == NULL) goto cleanup;
> +
> + new_wsd_attrib->Name = name;
> + new_wsd_attrib->Element = cur_element;
> + new_wsd_attrib->Next = NULL;
> +
> + if (cur_wsd_attrib == NULL)
> + element->FirstAttribute = new_wsd_attrib;
> + else
> + cur_wsd_attrib->Next = new_wsd_attrib;
> +
> + cur_wsd_attrib = new_wsd_attrib;
> + }
> +
> + cur_element = element;
> + break;
> +
> + case WS_XML_NODE_TYPE_TEXT:
> + text_node = (const WS_XML_TEXT_NODE *) node;
> +
> + if (cur_element == NULL)
> + {
> + WARN("No parent element open but encountered text element!\n");
> + continue;
> + }
> +
> + if (cur_element->FirstChild != NULL)
> + {
> + WARN("Text node encountered but parent already has child!\n");
> + continue;
> + }
> +
> + text = WSDAllocateLinkedMemory(element, sizeof(WSDXML_TEXT));
> + if (text == NULL) goto cleanup;
> +
> + text->Node.Parent = element;
> + text->Node.Next = NULL;
> + text->Node.Type = TextType;
> + text->Text = xml_text_to_wide_string(text, text_node->text);
> +
> + if (text->Text == NULL)
> + {
> + WARN("Text node returned null string.\n");
> + WSDFreeLinkedMemory(text);
> + continue;
> + }
> +
> + cur_element->FirstChild = (WSDXML_NODE *) text;
> + break;
> +
> + case WS_XML_NODE_TYPE_END_ELEMENT:
> + /* Go up a level to the parent element */
> + cur_element = cur_element->Node.Parent;
> + break;
> +
> + default:
> + break;
> + }
> + }
> +
> + return TRUE;
> +
> +cleanup:
> + /* Free uri and element_name if applicable */
> + WSDFreeLinkedMemory(uri);
> + return FALSE;
> +}
> +
> +static WSDXML_ELEMENT *find_element(WSDXML_ELEMENT *parent, LPCWSTR name, LPCWSTR ns_uri)
> +{
> + WSDXML_ELEMENT *cur = (WSDXML_ELEMENT *) parent->FirstChild;
> +
> + while (cur != NULL)
> + {
> + if ((lstrcmpW(cur->Name->LocalName, name) == 0) && (lstrcmpW(cur->Name->Space->Uri, ns_uri) == 0))
> + return cur;
> +
> + cur = (WSDXML_ELEMENT *) cur->Node.Next;
> + }
> +
> + return NULL;
> +}
> +
> int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg)
This is also looking like a good contender to return HRESULT and
take an [out] int *type parameter.
> {
> - /* TODO: Parse and read message */
> - return MSGTYPE_UNKNOWN;
> + WSDXML_ELEMENT *envelope = NULL, *header_element, *body_element;
> + WS_XML_READER_TEXT_ENCODING encoding;
> + WS_XML_ELEMENT_NODE *envelope_node;
> + WSD_SOAP_MESSAGE *soap_msg = NULL;
> + WS_XML_READER_BUFFER_INPUT input;
> + WS_XML_ATTRIBUTE *attrib = NULL;
> + IWSDXMLContext *context = NULL;
> + WS_XML_STRING *soap_uri = NULL;
> + const WS_XML_NODE *node;
> + WS_XML_READER *reader;
> + LPCWSTR value = NULL;
> + int i, message_type;
> + LPWSTR uri, prefix;
> + WS_HEAP *heap;
> + HRESULT ret;
> +
> + message_type = MSGTYPE_UNKNOWN;
> +
> + ret = WsCreateHeap(16384, 4096, NULL, 0, &heap, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + ret = WsCreateReader(NULL, 0, &reader, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + encoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
> + encoding.charSet = WS_CHARSET_AUTO;
> +
> + input.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
> + input.encodedData = (char *) xml;
> + input.encodedDataSize = xml_length;
> +
> + ret = WsSetInput(reader, (WS_XML_READER_ENCODING *) &encoding, (WS_XML_READER_INPUT *) &input, NULL, 0, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + soap_uri = populate_xml_string(envelopeNsUri);
> + if (soap_uri == NULL) goto cleanup;
> +
> + if (!move_to_element(reader, "Envelope", soap_uri)) goto cleanup;
> +
> + ret = WsGetReaderNode(reader, &node, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + if (node->nodeType != WS_XML_NODE_TYPE_ELEMENT) goto cleanup;
> +
> + envelope_node = (WS_XML_ELEMENT_NODE *) node;
> +
> + if (FAILED(WSDXMLCreateContext(&context))) goto cleanup;
> +
> + /* Find XML namespaces from the envelope element's attributes */
> + for (i = 0; i < envelope_node->attributeCount; i++)
> + {
> + attrib = envelope_node->attributes[i];
> +
> + if (attrib->isXmlNs)
> + {
> + uri = utf8_to_wide(NULL, (const char *) attrib->ns->bytes, attrib->ns->length);
> + if (uri == NULL) continue;
> +
> + prefix = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
> +
> + if (prefix == NULL)
> + {
> + WSDFreeLinkedMemory(uri);
> + continue;
> + }
> +
> + IWSDXMLContext_AddNamespace(context, uri, prefix, NULL);
> + WSDFreeLinkedMemory(uri);
> + }
> + }
> +
> + /* Create the SOAP message to return to the caller */
> + soap_msg = WSDAllocateLinkedMemory(NULL, sizeof(WSD_SOAP_MESSAGE));
> + if (soap_msg == NULL) goto cleanup;
> +
> + ZeroMemory(soap_msg, sizeof(WSD_SOAP_MESSAGE));
> +
> + envelope = WSDAllocateLinkedMemory(soap_msg, sizeof(WSDXML_ELEMENT));
> + if (envelope == NULL) goto cleanup;
> +
> + ZeroMemory(envelope, sizeof(WSDXML_ELEMENT));
> +
> + if (!ws_element_to_wsdxml_element(reader, context, envelope)) goto cleanup;
> +
> + /* Find the header element */
> + header_element = find_element(envelope, headerString, envelopeNsUri);
> + if (header_element == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, actionString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.Action = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.Action == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, toString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.To = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.To == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, messageIdString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.MessageID = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.MessageID == NULL) goto cleanup;
> +
> + /* Find the body element */
> + body_element = find_element(envelope, bodyString, envelopeNsUri);
> + if (body_element == NULL) goto cleanup;
> +
> + /* Now figure out which message we've been sent */
> + if (lstrcmpW(soap_msg->Header.Action, actionProbe) == 0)
> + {
> + /* TODO: Parse the Probe message */
> +
> + *out_msg = soap_msg;
> + soap_msg = NULL; /* caller will clean this up */
> + message_type = MSGTYPE_PROBE;
> + }
> +
> +cleanup:
> + free_xml_string(soap_uri);
> + WSDFreeLinkedMemory(soap_msg);
> + if (context != NULL) IWSDXMLContext_Release(context);
> +
> + return message_type;
> }
> diff --git a/dlls/wsdapi/tests/discovery.c b/dlls/wsdapi/tests/discovery.c
> index 96c2ead7f9..0599123a46 100644
> --- a/dlls/wsdapi/tests/discovery.c
> +++ b/dlls/wsdapi/tests/discovery.c
> @@ -491,7 +491,7 @@ static void verify_wsdxml_any_text(const char *debug_prefix, WSDXML_ELEMENT *any
> {
> WSDXML_TEXT *child;
>
> - ok(any != NULL, "%s: any == NULL\n", debug_prefix);
> + todo_wine ok(any != NULL, "%s: any == NULL\n", debug_prefix);
You'll need to have a really good reason for doing this. Can you
re-order things so you don't break the tests?
> if (any == NULL) return;
>
> child = (WSDXML_TEXT *) any->FirstChild;
> @@ -534,7 +534,7 @@ static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ProbeHandler(IWSDiscoveryP
> static const WCHAR extra_info[] = {'E','x','t','r','a','I','n','f','o',0};
> WSD_PROBE *probe_msg = (WSD_PROBE *) pSoap->Body;
>
> - ok(pSoap->Body != NULL, "pSoap->Body == NULL\n");
> + todo_wine ok(pSoap->Body != NULL, "pSoap->Body == NULL\n");
> ok(pSoap->Header.To != NULL && lstrcmpW(pSoap->Header.To, discoveryTo) == 0,
> "pSoap->Header.To == '%s'\n", wine_dbgstr_w(pSoap->Header.To));
> ok(pSoap->Header.Action != NULL && lstrcmpW(pSoap->Header.Action, actionProbe) == 0,
> @@ -939,7 +939,7 @@ after_publish_test:
> sprintf(probe_message, testProbeMessage, probe_uuid_str);
>
> ok(send_udp_multicast_of_type(probe_message, strlen(probe_message), AF_INET) == TRUE, "Sending Probe message failed\n");
> - todo_wine ok(WaitForSingleObject(probe_event, 2000) == WAIT_OBJECT_0, "Probe message not received\n");
> + ok(WaitForSingleObject(probe_event, 2000) == WAIT_OBJECT_0, "Probe message not received\n");
>
> RpcStringFreeA(&probe_uuid_str);
> }
>
>
June 7, 2018
Re: [PATCH 3/4] wsdapi: Add stub for Probe message parsing, prepare notification sink message.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:49PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/network.c | 52
> ++++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/soap.c | 6 +++++
> dlls/wsdapi/wsdapi_internal.h | 5 +++++
> 3 files changed, 62 insertions(+), 1 deletion(-)
>
>
> diff --git a/dlls/wsdapi/network.c b/dlls/wsdapi/network.c
> index 3e7509bfa3..7f6cee3ab0 100644
> --- a/dlls/wsdapi/network.c
> +++ b/dlls/wsdapi/network.c
> @@ -292,6 +292,56 @@ typedef struct listener_thread_params
> BOOL ipv6;
> } listener_thread_params;
>
> +static void process_received_message(listener_thread_params *params, char *message, int message_len,
> + SOCKADDR_STORAGE *source_addr)
This should return HRESULT.
> +{
> + IWSDUdpMessageParameters *msg_params = NULL;
> + IWSDUdpAddress *remote_addr = NULL;
> + struct notificationSink *sink;
> + WSD_SOAP_MESSAGE *msg = NULL;
> + int msg_type;
> +
> + msg_type = read_message(message, message_len, &msg);
> +
> + switch (msg_type)
> + {
> + case MSGTYPE_PROBE:
> + TRACE("Received probe message\n");
> +
> + if (FAILED(WSDCreateUdpMessageParameters(&msg_params)))
> + {
> + ERR("Unable to create IWSDUdpMessageParameters, not processing message.\n");
> + goto cleanup;
> + }
> +
> + if (FAILED(WSDCreateUdpAddress(&remote_addr)))
> + {
> + ERR("Unable to create IWSDUdpAddress, not processing message.\n");
> + goto cleanup;
> + }
> +
> + IWSDUdpAddress_SetSockaddr(remote_addr, source_addr);
> + IWSDUdpMessageParameters_SetRemoteAddress(msg_params, (IWSDAddress *)remote_addr);
> +
> + EnterCriticalSection(¶ms->impl->notification_sink_critical_section);
> +
> + LIST_FOR_EACH_ENTRY(sink, ¶ms->impl->notificationSinks, struct notificationSink, entry)
> + {
> + IWSDiscoveryPublisherNotify_ProbeHandler(sink->notificationSink, msg, (IWSDMessageParameters *)msg_params);
> + }
> +
> + LeaveCriticalSection(¶ms->impl->notification_sink_critical_section);
> +
> + break;
> + }
> +
> +cleanup:
> + WSDFreeLinkedMemory(msg);
> +
> + if (remote_addr != NULL) IWSDUdpAddress_Release(remote_addr);
> + if (msg_params != NULL) IWSDUdpMessageParameters_Release(msg_params);
> +}
> +
> #define RECEIVE_BUFFER_SIZE 65536
>
> static DWORD WINAPI listening_thread(LPVOID params)
> @@ -321,7 +371,7 @@ static DWORD WINAPI listening_thread(LPVOID params)
> }
> else
> {
> - /* TODO: Process received message */
> + process_received_message(parameter, buffer, bytes_received, &source_addr);
> }
> }
>
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index d0fb0afb0c..4e191a5c82 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -1080,3 +1080,9 @@ cleanup:
>
> return ret;
> }
> +
> +int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg)
> +{
> + /* TODO: Parse and read message */
> + return MSGTYPE_UNKNOWN;
> +}
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 3e5e163f3a..5c62627bba 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -72,6 +72,11 @@ HRESULT send_bye_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG
>
> HRESULT register_namespaces(IWSDXMLContext *xml_context);
>
> +int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg);
> +
> +#define MSGTYPE_UNKNOWN 0
> +#define MSGTYPE_PROBE 1
> +
> /* xml.c */
>
> LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value);
>
>
June 7, 2018
Re: [PATCH 1/4] wsdapi: Add support for listening for UDP multicast broadcasts.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:45PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/network.c | 278
> +++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/wsdapi_internal.h | 4 +
> 2 files changed, 281 insertions(+), 1 deletion(-)
>
>
> diff --git a/dlls/wsdapi/network.c b/dlls/wsdapi/network.c
> index f86b3b4a3e..3e7509bfa3 100644
> --- a/dlls/wsdapi/network.c
> +++ b/dlls/wsdapi/network.c
> @@ -231,11 +231,278 @@ BOOL send_udp_multicast(IWSDiscoveryPublisherImpl *impl, char *data, int length,
> return TRUE;
> }
>
> +static int join_multicast_group(SOCKET s, SOCKADDR_STORAGE *group, SOCKADDR_STORAGE *iface)
> +{
> + int level, optname, optlen;
> + struct ipv6_mreq mreqv6;
> + struct ip_mreq mreqv4;
> + char *optval;
> +
> + if (iface->ss_family == AF_INET6)
> + {
> + level = IPPROTO_IPV6;
> + optname = IPV6_ADD_MEMBERSHIP;
> + optval = (char *)&mreqv6;
> + optlen = sizeof(mreqv6);
> +
> + mreqv6.ipv6mr_multiaddr = ((SOCKADDR_IN6 *)group)->sin6_addr;
> + mreqv6.ipv6mr_interface = ((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> + }
> + else
> + {
> + level = IPPROTO_IP;
> + optname = IP_ADD_MEMBERSHIP;
> + optval = (char *)&mreqv4;
> + optlen = sizeof(mreqv4);
> +
> + mreqv4.imr_multiaddr.s_addr = ((SOCKADDR_IN *)group)->sin_addr.s_addr;
> + mreqv4.imr_interface.s_addr = ((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> + }
> +
> + return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +static int set_send_interface(SOCKET s, SOCKADDR_STORAGE *iface)
> +{
> + int level, optname, optlen;
> + char *optval = NULL;
> +
> + if (iface->ss_family == AF_INET6)
> + {
> + level = IPPROTO_IPV6;
> + optname = IPV6_MULTICAST_IF;
> + optval = (char *) &((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> + optlen = sizeof(((SOCKADDR_IN6 *)iface)->sin6_scope_id);
> + }
> + else
> + {
> + level = IPPROTO_IP;
> + optname = IP_MULTICAST_IF;
> + optval = (char *) &((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> + optlen = sizeof(((SOCKADDR_IN *)iface)->sin_addr.s_addr);
> + }
> +
> + return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +typedef struct listener_thread_params
> +{
> + IWSDiscoveryPublisherImpl *impl;
> + SOCKET listening_socket;
> + BOOL ipv6;
> +} listener_thread_params;
> +
> +#define RECEIVE_BUFFER_SIZE 65536
> +
> +static DWORD WINAPI listening_thread(LPVOID params)
> +{
> + listener_thread_params *parameter = (listener_thread_params *)params;
> + int bytes_received, address_len, err;
> + SOCKADDR_STORAGE source_addr;
> + char *buffer;
> +
> + buffer = heap_alloc(RECEIVE_BUFFER_SIZE);
> + address_len = parameter->ipv6 ? sizeof(SOCKADDR_IN6) : sizeof(SOCKADDR_IN);
> +
> + while (parameter->impl->publisherStarted)
> + {
> + bytes_received = recvfrom(parameter->listening_socket, buffer, RECEIVE_BUFFER_SIZE, 0,
> + (LPSOCKADDR) &source_addr, &address_len);
> +
> + if (bytes_received == SOCKET_ERROR)
> + {
> + err = WSAGetLastError();
> +
> + if (err != WSAETIMEDOUT)
> + {
> + WARN("Received error when trying to read from socket: %d. Stopping listener.\n", err);
> + return 0;
> + }
> + }
> + else
> + {
> + /* TODO: Process received message */
> + }
> + }
> +
> + /* The publisher has been stopped */
> + closesocket(parameter->listening_socket);
> +
> + heap_free(buffer);
> + heap_free(parameter);
> +
> + return 0;
> +}
> +
> +static void start_listening(IWSDiscoveryPublisherImpl *impl, SOCKADDR_STORAGE *bind_address)
This should probably return an indication of success (even if it's not
possible for the caller to do something sensible with the result).
> +{
> + SOCKADDR_STORAGE multicast_addr, bind_addr, interface_addr;
> + listener_thread_params *parameter = NULL;
> + const DWORD receive_timeout = 5000;
> + const UINT reuse_addr = 1;
> + HANDLE thread_handle;
> + int address_length;
> + SOCKET s = 0;
> +
> + TRACE("(%p, %p) family %d\n", impl, bind_address, bind_address->ss_family);
> +
> + /* Populate the multicast address */
> + ZeroMemory(&multicast_addr, sizeof(SOCKADDR_STORAGE));
> +
> + if (bind_address->ss_family == AF_INET)
> + {
> + SOCKADDR_IN *sockaddr4 = (SOCKADDR_IN *)&multicast_addr;
> +
> + sockaddr4->sin_port = htons(SEND_PORT);
> + sockaddr4->sin_addr.S_un.S_addr = htonl(SEND_ADDRESS_IPV4);
> + address_length = sizeof(SOCKADDR_IN);
> + }
> + else
> + {
> + SOCKADDR_IN6 *sockaddr6 = (SOCKADDR_IN6 *)&multicast_addr;
> +
> + sockaddr6->sin6_port = htons(SEND_PORT);
> + memcpy(&sockaddr6->sin6_addr, &send_address_ipv6, sizeof(send_address_ipv6));
> + address_length = sizeof(SOCKADDR_IN6);
> + }
> +
> + /* Update the port for the binding address */
> + memcpy(&bind_addr, bind_address, address_length);
> + ((SOCKADDR_IN *)&bind_addr)->sin_port = htons(SEND_PORT);
> +
> + /* Update the port for the interface address */
> + memcpy(&interface_addr, bind_address, address_length);
> + ((SOCKADDR_IN *)&interface_addr)->sin_port = htons(0);
> +
> + /* Create the socket */
> + s = socket(bind_address->ss_family, SOCK_DGRAM, IPPROTO_UDP);
> +
> + if (s == INVALID_SOCKET)
> + {
> + WARN("socket() failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Ensure the socket can be reused */
> + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr, sizeof(reuse_addr)) == SOCKET_ERROR)
> + {
> + WARN("setsockopt(SO_REUSEADDR) failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Bind the socket to the local interface so we can receive data */
> + if (bind(s, (struct sockaddr *)&bind_addr, address_length) == SOCKET_ERROR)
> + {
> + WARN("bind() failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Join the multicast group */
> + if (join_multicast_group(s, &multicast_addr, &interface_addr) == SOCKET_ERROR)
> + {
> + WARN("Unable to join multicast group (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Set the outgoing interface */
> + if (set_send_interface(s, &interface_addr) == SOCKET_ERROR)
> + {
> + WARN("Unable to set outgoing interface (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Set a 5-second receive timeout */
> + if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&receive_timeout, sizeof(receive_timeout)) == SOCKET_ERROR)
> + {
> + WARN("setsockopt(SO_RCVTIME0) failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Allocate memory for thread parameters */
> + parameter = heap_alloc(sizeof(listener_thread_params));
> +
> + parameter->impl = impl;
> + parameter->listening_socket = s;
> + parameter->ipv6 = (bind_address->ss_family == AF_INET6);
> +
> + thread_handle = CreateThread(NULL, 0, listening_thread, parameter, 0, NULL);
> +
> + if (thread_handle == NULL)
> + {
> + WARN("CreateThread failed (error %d)\n", GetLastError());
> + goto cleanup;
> + }
> +
> + impl->thread_handles[impl->num_thread_handles] = thread_handle;
> + impl->num_thread_handles++;
> +
> + return;
> +
> +cleanup:
> + closesocket(s);
> + heap_free(parameter);
> +}
> +
> +static BOOL start_listening_on_all_addresses(IWSDiscoveryPublisherImpl *impl, ULONG family)
> +{
> + IP_ADAPTER_ADDRESSES *adapter_addresses = NULL, *adapter_address;
> + ULONG bufferSize = 0;
> + ULONG ret;
> +
> + ret = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize); /* family should be AF_INET or AF_INET6 */
> +
> + if (ret != ERROR_BUFFER_OVERFLOW)
> + {
> + WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> + return FALSE;
> + }
> +
> + /* Get size of buffer for adapters */
> + adapter_addresses = (IP_ADAPTER_ADDRESSES *)heap_alloc(bufferSize);
> +
> + if (adapter_addresses == NULL)
> + {
> + WARN("Out of memory allocating space for adapter information\n");
> + return FALSE;
> + }
> +
> + /* Get list of adapters */
> + ret = GetAdaptersAddresses(family, 0, NULL, adapter_addresses, &bufferSize);
> +
> + if (ret != ERROR_SUCCESS)
> + {
> + WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> + goto cleanup;
> + }
> +
> + for (adapter_address = adapter_addresses; adapter_address != NULL; adapter_address = adapter_address->Next)
> + {
> + if (impl->num_thread_handles >= MAX_WSD_THREADS)
> + {
> + WARN("Exceeded maximum number of supported listener threads; too many network interfaces.");
> + goto cleanup;
> + }
> +
> + if (adapter_address->FirstUnicastAddress == NULL)
> + {
> + TRACE("No address found for adaptor '%s' (%p)\n", adapter_address->AdapterName, adapter_address);
> + continue;
> + }
> +
> + start_listening(impl, (SOCKADDR_STORAGE *)adapter_address->FirstUnicastAddress->Address.lpSockaddr);
> + }
> +
> +cleanup:
> + heap_free(adapter_addresses);
> + return ret == ERROR_SUCCESS;
> +}
> +
> void terminate_networking(IWSDiscoveryPublisherImpl *impl)
> {
> BOOL needsCleanup = impl->publisherStarted;
>
> impl->publisherStarted = FALSE;
> + WaitForMultipleObjects(impl->num_thread_handles, impl->thread_handles, TRUE, INFINITE);
>
> if (needsCleanup)
> WSACleanup();
> @@ -254,6 +521,15 @@ BOOL init_networking(IWSDiscoveryPublisherImpl *impl)
>
> impl->publisherStarted = TRUE;
>
> - /* TODO: Start listening */
> + if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV4) && (!start_listening_on_all_addresses(impl, AF_INET)))
> + goto cleanup;
> +
> + if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV6) && (!start_listening_on_all_addresses(impl, AF_INET6)))
> + goto cleanup;
> +
> return TRUE;
> +
> +cleanup:
> + terminate_networking(impl);
> + return FALSE;
> }
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 45d54c2ef1..f88822d5ef 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -40,6 +40,8 @@ struct notificationSink
> IWSDiscoveryPublisherNotify *notificationSink;
> };
>
> +#define MAX_WSD_THREADS 20
> +
> typedef struct IWSDiscoveryPublisherImpl {
> IWSDiscoveryPublisher IWSDiscoveryPublisher_iface;
> LONG ref;
> @@ -47,6 +49,8 @@ typedef struct IWSDiscoveryPublisherImpl {
> DWORD addressFamily;
> struct list notificationSinks;
> BOOL publisherStarted;
> + HANDLE thread_handles[MAX_WSD_THREADS];
> + int num_thread_handles;
> } IWSDiscoveryPublisherImpl;
>
> /* network.c */
>
>
June 7, 2018
[PATCH 2/2] schedsvc: Add support for running missed tasks at the service start.
by Dmitry Timoshkov
Signed-off-by: Dmitry Timoshkov <dmitry(a)baikal.ru>
---
dlls/schedsvc/atsvc.c | 24 ++++++++++++++++++++++++
dlls/schedsvc/schedsvc_private.h | 1 +
dlls/schedsvc/svc_main.c | 1 +
3 files changed, 26 insertions(+)
diff --git a/dlls/schedsvc/atsvc.c b/dlls/schedsvc/atsvc.c
index da837d8a8e..6c6e3e109e 100644
--- a/dlls/schedsvc/atsvc.c
+++ b/dlls/schedsvc/atsvc.c
@@ -1033,6 +1033,30 @@ void check_task_time(void)
LeaveCriticalSection(&at_job_list_section);
}
+void check_missed_task_time(void)
+{
+ FILETIME current_ft, last_ft;
+ struct job_t *job;
+
+ GetSystemTimeAsFileTime(¤t_ft);
+ FileTimeToLocalFileTime(¤t_ft, ¤t_ft);
+
+ EnterCriticalSection(&at_job_list_section);
+
+ LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
+ {
+ if (SystemTimeToFileTime(&job->data.last_runtime, &last_ft))
+ {
+ if (job_runs_at(job, &last_ft, ¤t_ft))
+ {
+ run_job(job);
+ }
+ }
+ }
+
+ LeaveCriticalSection(&at_job_list_section);
+}
+
void remove_job(const WCHAR *name)
{
struct job_t *job;
diff --git a/dlls/schedsvc/schedsvc_private.h b/dlls/schedsvc/schedsvc_private.h
index 10892d55ca..117c6f51ed 100644
--- a/dlls/schedsvc/schedsvc_private.h
+++ b/dlls/schedsvc/schedsvc_private.h
@@ -31,6 +31,7 @@ void update_process_status(DWORD pid) DECLSPEC_HIDDEN;
BOOL get_next_runtime(LARGE_INTEGER *rt) DECLSPEC_HIDDEN;
void check_task_time(void) DECLSPEC_HIDDEN;
void load_at_tasks(void) DECLSPEC_HIDDEN;
+void check_missed_task_time(void) DECLSPEC_HIDDEN;
static inline WCHAR *heap_strdupW(const WCHAR *src)
{
diff --git a/dlls/schedsvc/svc_main.c b/dlls/schedsvc/svc_main.c
index 2bba674b07..8e23a049db 100644
--- a/dlls/schedsvc/svc_main.c
+++ b/dlls/schedsvc/svc_main.c
@@ -54,6 +54,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
TRACE("Starting...\n");
load_at_tasks();
+ check_missed_task_time();
htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
if (htimer == NULL)
--
2.16.3
June 7, 2018
[PATCH 1/2] schedsvc: Load job files at the service start up.
by Dmitry Timoshkov
Signed-off-by: Dmitry Timoshkov <dmitry(a)baikal.ru>
---
dlls/schedsvc/atsvc.c | 41 ++++++++++++++++++++++++++++++++++++++++
dlls/schedsvc/schedsvc_private.h | 1 +
dlls/schedsvc/svc_main.c | 2 ++
3 files changed, 44 insertions(+)
diff --git a/dlls/schedsvc/atsvc.c b/dlls/schedsvc/atsvc.c
index 91ef3edca0..da837d8a8e 100644
--- a/dlls/schedsvc/atsvc.c
+++ b/dlls/schedsvc/atsvc.c
@@ -617,6 +617,47 @@ void add_job(const WCHAR *name)
LeaveCriticalSection(&at_job_list_section);
}
+static inline BOOL is_file(const WIN32_FIND_DATAW *data)
+{
+ return !(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+void load_at_tasks(void)
+{
+ static const WCHAR tasksW[] = { '\\','T','a','s','k','s','\\',0 };
+ static const WCHAR allW[] = { '*',0 };
+ WCHAR windir[MAX_PATH], path[MAX_PATH];
+ WIN32_FIND_DATAW data;
+ HANDLE handle;
+
+ GetWindowsDirectoryW(windir, MAX_PATH);
+ lstrcpyW(path, windir);
+ lstrcatW(path, tasksW);
+ lstrcatW(path, allW);
+
+ handle = FindFirstFileW(path, &data);
+ if (handle == INVALID_HANDLE_VALUE) return;
+
+ do
+ {
+ if (is_file(&data))
+ {
+ lstrcpyW(path, windir);
+ lstrcatW(path, tasksW);
+
+ if (lstrlenW(path) + lstrlenW(data.cFileName) < MAX_PATH)
+ {
+ lstrcatW(path, data.cFileName);
+ add_job(path);
+ }
+ else
+ FIXME("too long file name %s\n", debugstr_w(data.cFileName));
+ }
+ } while (FindNextFileW(handle, &data));
+
+ FindClose(handle);
+}
+
static BOOL write_signature(HANDLE hfile)
{
struct
diff --git a/dlls/schedsvc/schedsvc_private.h b/dlls/schedsvc/schedsvc_private.h
index d601030e78..10892d55ca 100644
--- a/dlls/schedsvc/schedsvc_private.h
+++ b/dlls/schedsvc/schedsvc_private.h
@@ -30,6 +30,7 @@ void add_process_to_queue(HANDLE hproc) DECLSPEC_HIDDEN;
void update_process_status(DWORD pid) DECLSPEC_HIDDEN;
BOOL get_next_runtime(LARGE_INTEGER *rt) DECLSPEC_HIDDEN;
void check_task_time(void) DECLSPEC_HIDDEN;
+void load_at_tasks(void) DECLSPEC_HIDDEN;
static inline WCHAR *heap_strdupW(const WCHAR *src)
{
diff --git a/dlls/schedsvc/svc_main.c b/dlls/schedsvc/svc_main.c
index 0dfac818fd..2bba674b07 100644
--- a/dlls/schedsvc/svc_main.c
+++ b/dlls/schedsvc/svc_main.c
@@ -53,6 +53,8 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
TRACE("Starting...\n");
+ load_at_tasks();
+
htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
if (htimer == NULL)
{
--
2.16.3
June 7, 2018
[PATCH 4/4] testbot: Switch Patches::Submit() to the new job staging standard.
by Francois Gouget
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/lib/WineTestBot/Patches.pm | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/testbot/lib/WineTestBot/Patches.pm b/testbot/lib/WineTestBot/Patches.pm
index 4f428133f..e349dfe88 100644
--- a/testbot/lib/WineTestBot/Patches.pm
+++ b/testbot/lib/WineTestBot/Patches.pm
@@ -166,7 +166,6 @@ sub Submit($$$)
# Create a new job for this patch
my $NewJob = $Jobs->Add();
- $NewJob->Status("queued");
$NewJob->User($User);
$NewJob->Priority(6);
my $PropertyDescriptor = $Jobs->GetPropertyDescriptorByName("Remarks");
@@ -181,11 +180,9 @@ sub Submit($$$)
# Add build step to the job
my $Steps = $NewJob->Steps;
my $NewStep = $Steps->Add();
- # Create a link to the patch file in the staging dir
- my $StagingFileName = CreateNewLink($PatchFileName, "$DataDir/staging", "_patch.diff");
- $NewStep->FileName(basename($StagingFileName));
+ $NewStep->FileName("patch.diff");
$NewStep->FileType($TestInfo->{Type});
- $NewStep->InStaging(1);
+ $NewStep->InStaging(!1);
$NewStep->Type("build");
$NewStep->DebugLevel(0);
@@ -198,7 +195,7 @@ sub Submit($$$)
$Task->VM($BuildVM);
$Task->Timeout($BuildTimeout);
- # Save this step (&job+task) so the others can reference it
+ # Save the build step so other steps can reference it
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined($ErrMessage))
{
@@ -206,6 +203,13 @@ sub Submit($$$)
return $ErrMessage;
}
+ # Stage the patch so it can be picked up by the job
+ if (!link($PatchFileName, "$DataDir/staging/job". $NewJob->Id ."_patch.diff"))
+ {
+ $self->Disposition("Failed to prepare patch file");
+ return $!;
+ }
+
foreach my $Unit (sort keys %{$TestInfo->{Units}})
{
# Add 32 and 64-bit tasks
@@ -217,7 +221,7 @@ sub Submit($$$)
if (@{$VMs->GetKeys()})
{
# Create the corresponding Step
- $NewStep = $Steps->Add();
+ my $NewStep = $Steps->Add();
$NewStep->PreviousNo(1);
my $FileName = $TestInfo->{ExeBase};
$FileName .= "64" if ($Bits eq "64");
@@ -240,13 +244,23 @@ sub Submit($$$)
}
}
+ # Save it all
($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
- if (defined($ErrMessage))
+ if (defined $ErrMessage)
{
$self->Disposition("Failed to submit job");
return $ErrMessage;
}
+ # Switch Status to staging to indicate we are done setting up the job
+ $NewJob->Status("staging");
+ ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined $ErrMessage)
+ {
+ $self->Disposition("Failed to submit job (staging)");
+ return $ErrMessage;
+ }
+
if ($First)
{
$First = !1;
--
2.17.0
June 6, 2018
[PATCH 3/4] testbot/web: Switch Submit to the new job staging standard.
by Francois Gouget
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/web/Submit.pl | 63 ++++++++++++++++++++-----------------------
1 file changed, 29 insertions(+), 34 deletions(-)
diff --git a/testbot/web/Submit.pl b/testbot/web/Submit.pl
index 8e22621fd..d5265a3b3 100644
--- a/testbot/web/Submit.pl
+++ b/testbot/web/Submit.pl
@@ -753,28 +753,11 @@ sub OnSubmit($)
my $BaseName = $self->ValidateAndGetFileName("FileName");
return !1 if (!$BaseName);
- # Store the file in the staging directory until the relevant Job and Step
- # IDs are known and it can be moved to the jobs directory tree. But rename
- # it so it does not get overwritten if the user submits another one before
- # the Engine gets around to doing so.
- my $StagingFileName = CreateNewFile("$DataDir/staging", "_$BaseName");
-
- my $TmpStagingFullPath = $self->GetTmpStagingFullPath($BaseName);
- if ($StagingFileName and !rename($TmpStagingFullPath, $StagingFileName))
- {
- $self->{ErrMessage} = "Could not rename '$TmpStagingFullPath' to '$StagingFileName': $!\n";
- unlink($StagingFileName);
- $StagingFileName = undef;
- }
- # If needed fall back to the existing staging file and hope for the best.
- $StagingFileName = basename($StagingFileName || $TmpStagingFullPath);
-
# See also Patches::Submit() in lib/WineTestBot/Patches.pm
# First create a new job
my $Jobs = CreateJobs();
my $NewJob = $Jobs->Add();
- $NewJob->Status("queued");
$NewJob->User($self->GetCurrentSession()->User);
$NewJob->Priority(5);
if ($self->GetParam("Remarks"))
@@ -792,15 +775,15 @@ sub OnSubmit($)
}
my $Steps = $NewJob->Steps;
+ my $BuildStep;
my $FileType = $self->GetParam("FileType");
- my $BuildStepNo;
if ($FileType eq "patchdlls" || $FileType eq "patchprograms")
{
# This is a patch so add a build step...
- my $BuildStep = $Steps->Add();
- $BuildStep->FileName($StagingFileName);
+ $BuildStep = $Steps->Add();
+ $BuildStep->FileName($BaseName);
$BuildStep->FileType($FileType);
- $BuildStep->InStaging(1);
+ $BuildStep->InStaging(!1);
$BuildStep->Type("build");
$BuildStep->DebugLevel(0);
@@ -813,14 +796,13 @@ sub OnSubmit($)
$Task->VM($BuildVM);
$Task->Timeout($BuildTimeout);
- # Save this step (&job+task) so the others can reference it
+ # Save the build step so the others can reference it
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined($ErrMessage))
{
$self->{ErrMessage} = $ErrMessage;
return !1;
}
- $BuildStepNo = 1;
}
# Add steps and tasks for the 32 and 64-bit tests
@@ -844,22 +826,18 @@ sub OnSubmit($)
{
# First create the test step
my $TestStep = $Steps->Add();
- $TestStep->PreviousNo($BuildStepNo);
if ($FileType eq "patchdlls" || $FileType eq "patchprograms")
{
- my $FileName=$self->GetParam("TestExecutable");
- if ($Bits eq "64")
- {
- $FileName =~ s/_test\.exe$/_test64.exe/;
- }
- $TestStep->FileName($FileName);
- $TestStep->InStaging(!1);
+ $TestStep->PreviousNo($BuildStep->No);
+ my $TestExe = basename($self->GetParam("TestExecutable"));
+ $TestExe =~ s/_test\.exe$/_test64.exe/ if ($Bits eq "64");
+ $TestStep->FileName($TestExe);
}
else
{
- $TestStep->FileName($StagingFileName);
- $TestStep->InStaging(1);
+ $TestStep->FileName($BaseName);
}
+ $TestStep->InStaging(!1);
$TestStep->FileType("exe$Bits");
$TestStep->Type("single");
$TestStep->DebugLevel($self->GetParam("DebugLevel"));
@@ -875,7 +853,7 @@ sub OnSubmit($)
}
}
- # Now save the whole thing (or whatever's left to save)
+ # Now save it all (or whatever's left to save)
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined($ErrMessage))
{
@@ -883,6 +861,23 @@ sub OnSubmit($)
return !1;
}
+ # Stage the test patch/executable so the job can pick it up
+ my $TmpStagingFullPath = $self->GetTmpStagingFullPath($BaseName);
+ if (!rename($TmpStagingFullPath, "$DataDir/staging/job". $NewJob->Id ."_$BaseName"))
+ {
+ $self->{ErrMessage} = "Could not stage '$BaseName': $!\n";
+ return !1;
+ }
+
+ # Switch Status to staging to indicate we are done setting up the job
+ $NewJob->Status("staging");
+ ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined($ErrMessage))
+ {
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
+ }
+
# Notify engine
my $ErrMessage = RescheduleJobs();
if (defined $ErrMessage)
--
2.17.0
June 6, 2018
[PATCH 2/4] testbot: Switch CheckForWinetestUpdate.pl to the new job staging standard.
by Francois Gouget
The new approach ensures there is no race condition with the TestBot
Engine between saving the job and saving the steps.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/bin/CheckForWinetestUpdate.pl | 46 +++++++++++++++++++--------
1 file changed, 32 insertions(+), 14 deletions(-)
diff --git a/testbot/bin/CheckForWinetestUpdate.pl b/testbot/bin/CheckForWinetestUpdate.pl
index 17dab8236..137db572c 100755
--- a/testbot/bin/CheckForWinetestUpdate.pl
+++ b/testbot/bin/CheckForWinetestUpdate.pl
@@ -192,15 +192,9 @@ sub AddJob($$$)
return 1;
}
- # Create a hard link in staging so it can then be moved into the job
- # directory. This is ok because the latest file is never overwritten.
- my $StagingFileName = CreateNewLink("$DataDir/latest/$LatestBaseName",
- "$DataDir/staging", "_$LatestBaseName");
-
# First create a new job
my $Jobs = CreateJobs();
my $NewJob = $Jobs->Add();
- $NewJob->Status("queued");
$NewJob->User(GetBatchUser());
$NewJob->Priority($BaseJob && $Bits == 32 ? 8 : 9);
$NewJob->Remarks($Remarks);
@@ -210,9 +204,9 @@ sub AddJob($$$)
my $NewStep = $Steps->Add();
my $BitsSuffix = ($Bits == 64 ? "64" : "");
$NewStep->Type("suite");
- $NewStep->FileName(basename($StagingFileName));
+ $NewStep->FileName($LatestBaseName);
$NewStep->FileType($Bits == 64 ? "exe64" : "exe32");
- $NewStep->InStaging(1);
+ $NewStep->InStaging(!1);
# Add a task for each VM
my $Tasks = $NewStep->Tasks;
@@ -224,12 +218,28 @@ sub AddJob($$$)
$Task->Timeout($SuiteTimeout);
}
- # Now save the whole thing
+ # Save it all
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined $ErrMessage)
{
- Error "Failed to save job: $ErrMessage\n";
- unlink($StagingFileName);
+ LogMsg "Failed to save the $LatestBaseName job: $ErrMessage\n";
+ return 0;
+ }
+
+ # Stage the test file so it can be picked up by the job
+ if (!link("$DataDir/latest/$LatestBaseName",
+ "$DataDir/staging/job". $NewJob->Id ."_$LatestBaseName"))
+ {
+ Error "Failed to stage $LatestBaseName: $!\n";
+ return 0;
+ }
+
+ # Switch Status to staging to indicate we are done setting up the job
+ $NewJob->Status("staging");
+ ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined $ErrMessage)
+ {
+ Error "Failed to save the $LatestBaseName job (staging): $ErrMessage\n";
return 0;
}
@@ -244,7 +254,6 @@ sub AddReconfigJob()
# First create a new job
my $Jobs = CreateJobs();
my $NewJob = $Jobs->Add();
- $NewJob->Status("queued");
$NewJob->User(GetBatchUser());
$NewJob->Priority(3);
$NewJob->Remarks($Remarks);
@@ -266,11 +275,20 @@ sub AddReconfigJob()
$Task->VM($BuildVM);
$Task->Timeout($ReconfigTimeout);
- # Now save the whole thing
+ # Save it all
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined $ErrMessage)
{
- Error "Failed to save reconfig job: $ErrMessage\n";
+ Error "Failed to save the Reconfig job: $ErrMessage\n";
+ return 0;
+ }
+
+ # Switch Status to staging to indicate we are done setting up the job
+ $NewJob->Status("staging");
+ ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined $ErrMessage)
+ {
+ Error "Failed to save the Reconfig job (staging): $ErrMessage\n";
return 0;
}
}
--
2.17.0
June 6, 2018