@@ -36,6 +36,7 @@ static const CHAR spooler[] = "Spooler"; /* Should be available on all platforms
36
36
static CHAR selfname [MAX_PATH ];
37
37
38
38
static BOOL (WINAPI * pChangeServiceConfig2A )(SC_HANDLE ,DWORD ,LPVOID );
39
+ static BOOL (WINAPI * pChangeServiceConfig2W )(SC_HANDLE ,DWORD ,LPVOID );
39
40
static BOOL (WINAPI * pEnumServicesStatusExA )(SC_HANDLE , SC_ENUM_TYPE , DWORD ,
40
41
DWORD , LPBYTE , DWORD , LPDWORD ,
41
42
LPDWORD , LPDWORD , LPCSTR );
@@ -57,6 +58,7 @@ static void init_function_pointers(void)
57
58
HMODULE hadvapi32 = GetModuleHandleA ("advapi32.dll" );
58
59
59
60
pChangeServiceConfig2A = (void * )GetProcAddress (hadvapi32 , "ChangeServiceConfig2A" );
61
+ pChangeServiceConfig2W = (void * )GetProcAddress (hadvapi32 , "ChangeServiceConfig2W" );
60
62
pEnumServicesStatusExA = (void * )GetProcAddress (hadvapi32 , "EnumServicesStatusExA" );
61
63
pEnumServicesStatusExW = (void * )GetProcAddress (hadvapi32 , "EnumServicesStatusExW" );
62
64
pGetSecurityInfo = (void * )GetProcAddress (hadvapi32 , "GetSecurityInfo" );
@@ -1954,13 +1956,17 @@ static void test_queryconfig2(void)
1954
1956
DWORD expected , needed ;
1955
1957
BYTE buffer [MAX_PATH ];
1956
1958
LPSERVICE_DESCRIPTIONA pConfig = (LPSERVICE_DESCRIPTIONA )buffer ;
1959
+ LPSERVICE_DESCRIPTIONW pConfigW = (LPSERVICE_DESCRIPTIONW )buffer ;
1957
1960
SERVICE_PRESHUTDOWN_INFO preshutdown_info ;
1958
1961
static const CHAR servicename [] = "Winetest" ;
1959
1962
static const CHAR displayname [] = "Winetest dummy service" ;
1960
1963
static const CHAR pathname [] = "we_dont_care.exe" ;
1961
1964
static const CHAR dependencies [] = "Master1\0Master2\0+MasterGroup1\0" ;
1962
1965
static const CHAR password [] = "" ;
1963
1966
static const CHAR description [] = "Description" ;
1967
+ static const CHAR description_empty [] = "" ;
1968
+ static const WCHAR descriptionW [] = {'D' ,'e' ,'s' ,'c' ,'r' ,'i' ,'p' ,'t' ,'i' ,'o' ,'n' ,'W' ,0 };
1969
+ static const WCHAR descriptionW_empty [] = {0 };
1964
1970
1965
1971
if (!pQueryServiceConfig2A )
1966
1972
{
@@ -2121,6 +2127,66 @@ static void test_queryconfig2(void)
2121
2127
ret = pQueryServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION ,buffer , needed ,& needed );
2122
2128
ok (ret , "expected QueryServiceConfig2W to succeed\n" );
2123
2129
2130
+ pConfig -> lpDescription = (LPSTR )description ;
2131
+ ret = pChangeServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2132
+ ok (ret , "expected ChangeServiceConfig2A to succeed\n" );
2133
+
2134
+ pConfig -> lpDescription = NULL ;
2135
+ ret = pQueryServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2136
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2137
+ ok (pConfig -> lpDescription && !strcmp (description , pConfig -> lpDescription ),
2138
+ "expected lpDescription to be %s, got %s\n" , description , pConfig -> lpDescription );
2139
+
2140
+ pConfig -> lpDescription = NULL ;
2141
+ ret = pChangeServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2142
+ ok (ret , "expected ChangeServiceConfig2A to succeed\n" );
2143
+
2144
+ pConfig -> lpDescription = NULL ;
2145
+ ret = pQueryServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2146
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2147
+ ok (pConfig -> lpDescription && !strcmp (description , pConfig -> lpDescription ),
2148
+ "expected lpDescription to be %s, got %s\n" , description , pConfig -> lpDescription );
2149
+
2150
+ pConfig -> lpDescription = (LPSTR )description_empty ;
2151
+ ret = pChangeServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2152
+ ok (ret , "expected ChangeServiceConfig2A to succeed\n" );
2153
+
2154
+ pConfig -> lpDescription = (void * )0xdeadbeef ;
2155
+ ret = pQueryServiceConfig2A (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2156
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2157
+ ok (!pConfig -> lpDescription ,
2158
+ "expected lpDescription to be null, got %s\n" , pConfig -> lpDescription );
2159
+
2160
+ pConfigW -> lpDescription = (LPWSTR )descriptionW ;
2161
+ ret = pChangeServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2162
+ ok (ret , "expected ChangeServiceConfig2W to succeed\n" );
2163
+
2164
+ pConfigW -> lpDescription = NULL ;
2165
+ ret = pQueryServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2166
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2167
+ ok (pConfigW -> lpDescription && !lstrcmpW (descriptionW , pConfigW -> lpDescription ),
2168
+ "expected lpDescription to be %s, got %s\n" , wine_dbgstr_w (descriptionW ), wine_dbgstr_w (pConfigW -> lpDescription ));
2169
+
2170
+ pConfigW -> lpDescription = NULL ;
2171
+ ret = pChangeServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2172
+ ok (ret , "expected ChangeServiceConfig2W to succeed\n" );
2173
+
2174
+ pConfigW -> lpDescription = NULL ;
2175
+ ret = pQueryServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2176
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2177
+ ok (pConfigW -> lpDescription && !lstrcmpW (descriptionW , pConfigW -> lpDescription ),
2178
+ "expected lpDescription to be %s, got %s\n" , wine_dbgstr_w (descriptionW ), wine_dbgstr_w (pConfigW -> lpDescription ));
2179
+
2180
+ pConfigW -> lpDescription = (LPWSTR )descriptionW_empty ;
2181
+ ret = pChangeServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , & buffer );
2182
+ ok (ret , "expected ChangeServiceConfig2W to succeed\n" );
2183
+
2184
+ pConfigW -> lpDescription = (void * )0xdeadbeef ;
2185
+ ret = pQueryServiceConfig2W (svc_handle , SERVICE_CONFIG_DESCRIPTION , buffer , sizeof (buffer ), & needed );
2186
+ ok (ret , "expected QueryServiceConfig2A to succeed\n" );
2187
+ ok (!pConfigW -> lpDescription ,
2188
+ "expected lpDescription to be null, got %s\n" , wine_dbgstr_w (pConfigW -> lpDescription ));
2189
+
2124
2190
SetLastError (0xdeadbeef );
2125
2191
ret = pQueryServiceConfig2W (svc_handle , SERVICE_CONFIG_PRESHUTDOWN_INFO ,
2126
2192
(LPBYTE )& preshutdown_info , sizeof (preshutdown_info ), & needed );
@@ -2200,73 +2266,146 @@ static DWORD try_start_stop(SC_HANDLE svc_handle, const char* name, DWORD is_nt4
2200
2266
return le1 ;
2201
2267
}
2202
2268
2269
+ #define PHASE_STOPPED 1
2270
+ #define PHASE_RUNNING 2
2271
+
2203
2272
struct notify_data {
2204
2273
SERVICE_NOTIFYW notify ;
2205
2274
SC_HANDLE svc ;
2275
+ BOOL was_called ;
2276
+ DWORD phase ;
2206
2277
};
2207
2278
2208
- static void CALLBACK cb_stopped (void * user )
2279
+ static void CALLBACK notify_cb (void * user )
2209
2280
{
2210
2281
struct notify_data * data = user ;
2211
- BOOL br ;
2282
+ switch (data -> phase )
2283
+ {
2284
+ case PHASE_STOPPED :
2285
+ ok (data -> notify .dwNotificationStatus == ERROR_SUCCESS ,
2286
+ "Got wrong notification status: %u\n" , data -> notify .dwNotificationStatus );
2287
+ ok (data -> notify .ServiceStatus .dwCurrentState == SERVICE_STOPPED ,
2288
+ "Got wrong service state: 0x%x\n" , data -> notify .ServiceStatus .dwCurrentState );
2289
+ ok (data -> notify .dwNotificationTriggered == SERVICE_NOTIFY_STOPPED ,
2290
+ "Got wrong notification triggered: 0x%x\n" , data -> notify .dwNotificationTriggered );
2291
+ break ;
2212
2292
2213
- ok (data -> notify .dwNotificationStatus == ERROR_SUCCESS ,
2214
- "Got wrong notification status: %u\n" , data -> notify .dwNotificationStatus );
2215
- ok (data -> notify .ServiceStatus .dwCurrentState == SERVICE_STOPPED ,
2216
- "Got wrong service state: 0x%x\n" , data -> notify .ServiceStatus .dwCurrentState );
2217
- ok (data -> notify .dwNotificationTriggered == SERVICE_NOTIFY_STOPPED ,
2218
- "Got wrong notification triggered: 0x%x\n" , data -> notify .dwNotificationTriggered );
2293
+ case PHASE_RUNNING :
2294
+ ok (data -> notify .dwNotificationStatus == ERROR_SUCCESS ,
2295
+ "Got wrong notification status: %u\n" , data -> notify .dwNotificationStatus );
2296
+ ok (data -> notify .ServiceStatus .dwCurrentState == SERVICE_RUNNING ,
2297
+ "Got wrong service state: 0x%x\n" , data -> notify .ServiceStatus .dwCurrentState );
2298
+ ok (data -> notify .dwNotificationTriggered == SERVICE_NOTIFY_RUNNING ,
2299
+ "Got wrong notification triggered: 0x%x\n" , data -> notify .dwNotificationTriggered );
2300
+ break ;
2301
+ }
2219
2302
2220
- br = StartServiceA (data -> svc , 0 , NULL );
2221
- ok (br , "StartService failed: %u\n" , GetLastError ());
2303
+ data -> was_called = TRUE;
2222
2304
}
2223
2305
2224
- static void CALLBACK cb_running ( void * user )
2306
+ static void test_servicenotify ( SC_HANDLE scm_handle , const char * servicename )
2225
2307
{
2226
- struct notify_data * data = user ;
2308
+ DWORD dr , dr2 ;
2309
+ struct notify_data data ;
2310
+ struct notify_data data2 ;
2227
2311
BOOL br ;
2228
2312
SERVICE_STATUS status ;
2229
-
2230
- ok (data -> notify .dwNotificationStatus == ERROR_SUCCESS ,
2231
- "Got wrong notification status: %u\n" , data -> notify .dwNotificationStatus );
2232
- ok (data -> notify .ServiceStatus .dwCurrentState == SERVICE_RUNNING ,
2233
- "Got wrong service state: 0x%x\n" , data -> notify .ServiceStatus .dwCurrentState );
2234
- ok (data -> notify .dwNotificationTriggered == SERVICE_NOTIFY_RUNNING ,
2235
- "Got wrong notification triggered: 0x%x\n" , data -> notify .dwNotificationTriggered );
2236
-
2237
- br = ControlService (data -> svc , SERVICE_CONTROL_STOP , & status );
2238
- ok (br , "ControlService failed: %u\n" , GetLastError ());
2239
- }
2240
-
2241
- static void test_servicenotify (SC_HANDLE svc )
2242
- {
2243
- DWORD dr ;
2244
- struct notify_data data ;
2313
+ HANDLE svc , svc2 ;
2245
2314
2246
2315
if (!pNotifyServiceStatusChangeW ){
2247
2316
win_skip ("No NotifyServiceStatusChangeW\n" );
2248
2317
return ;
2249
2318
}
2250
2319
2320
+ svc = OpenServiceA (scm_handle , servicename , GENERIC_ALL );
2321
+ svc2 = OpenServiceA (scm_handle , servicename , GENERIC_ALL );
2322
+ ok (svc != NULL && svc2 != NULL , "Failed to open service\n" );
2323
+ if (!svc || !svc2 )
2324
+ return ;
2325
+
2326
+ /* receive stopped notification, then start service */
2251
2327
memset (& data .notify , 0 , sizeof (data .notify ));
2252
2328
data .notify .dwVersion = SERVICE_NOTIFY_STATUS_CHANGE ;
2253
- data .notify .pfnNotifyCallback = & cb_stopped ;
2329
+ data .notify .pfnNotifyCallback = & notify_cb ;
2254
2330
data .notify .pContext = & data ;
2255
2331
data .svc = svc ;
2332
+ data .phase = PHASE_STOPPED ;
2333
+ data .was_called = FALSE;
2334
+
2335
+ dr = pNotifyServiceStatusChangeW (svc , SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING , & data .notify );
2336
+ ok (dr == ERROR_SUCCESS , "NotifyServiceStatusChangeW failed: %u\n" , dr );
2337
+
2338
+ dr = SleepEx (100 , TRUE);
2339
+ ok (dr == WAIT_IO_COMPLETION , "Got wrong SleepEx result: %u\n" , dr );
2340
+ ok (data .was_called == TRUE, "APC wasn't called\n" );
2341
+
2342
+ br = StartServiceA (svc , 0 , NULL );
2343
+ ok (br , "StartService failed: %u\n" , GetLastError ());
2344
+
2345
+ /* receive running notification */
2346
+ data .phase = PHASE_RUNNING ;
2347
+ data .was_called = FALSE;
2348
+
2349
+ dr = pNotifyServiceStatusChangeW (svc , SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING , & data .notify );
2350
+ ok (dr == ERROR_SUCCESS , "NotifyServiceStatusChangeW failed: %u\n" , dr );
2351
+
2352
+ dr = SleepEx (100 , TRUE);
2353
+ ok (dr == WAIT_IO_COMPLETION , "Got wrong SleepEx result: %u\n" , dr );
2354
+ ok (data .was_called == TRUE, "APC wasn't called\n" );
2355
+
2356
+ /* cannot register two notifications */
2357
+ data .phase = PHASE_STOPPED ;
2358
+ data .was_called = FALSE;
2256
2359
2257
2360
dr = pNotifyServiceStatusChangeW (svc , SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING , & data .notify );
2258
2361
ok (dr == ERROR_SUCCESS , "NotifyServiceStatusChangeW failed: %u\n" , dr );
2259
2362
2363
+ memset (& data2 .notify , 0 , sizeof (data2 .notify ));
2364
+ data2 .notify .dwVersion = SERVICE_NOTIFY_STATUS_CHANGE ;
2365
+ data2 .notify .pfnNotifyCallback = & notify_cb ;
2366
+ data2 .notify .pContext = & data2 ;
2367
+
2368
+ dr = pNotifyServiceStatusChangeW (svc , SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING , & data2 .notify );
2369
+ ok (dr == ERROR_SUCCESS || /* win8+ */
2370
+ dr == ERROR_ALREADY_REGISTERED , "NotifyServiceStatusChangeW gave wrong result: %u\n" , dr );
2371
+
2372
+ /* should receive no notification because status has not changed.
2373
+ * on win8+, SleepEx quits early but the callback is still not invoked. */
2374
+ dr2 = SleepEx (100 , TRUE);
2375
+ ok ((dr == ERROR_SUCCESS && dr2 == WAIT_IO_COMPLETION ) || /* win8+ */
2376
+ (dr == ERROR_ALREADY_REGISTERED && dr2 == 0 ), "Got wrong SleepEx result: %u\n" , dr );
2377
+ ok (data .was_called == FALSE, "APC should not have been called\n" );
2378
+
2379
+ /* stop service and receive notifiction */
2380
+ br = ControlService (svc , SERVICE_CONTROL_STOP , & status );
2381
+ ok (br , "ControlService failed: %u\n" , GetLastError ());
2382
+
2260
2383
dr = SleepEx (100 , TRUE);
2261
- ok (dr == WAIT_IO_COMPLETION , "APC wasn't called\n" );
2384
+ ok (dr == WAIT_IO_COMPLETION , "Got wrong SleepEx result: %u\n" , dr );
2385
+ ok (data .was_called == TRUE, "APC wasn't called\n" );
2262
2386
2263
- data .notify .pfnNotifyCallback = & cb_running ;
2387
+ /* test cancelation: create notify on svc that will block until service
2388
+ * start; close svc; start service on svc2; verify that notification does
2389
+ * not happen */
2264
2390
2391
+ data .phase = PHASE_RUNNING ;
2392
+ data .was_called = FALSE;
2265
2393
dr = pNotifyServiceStatusChangeW (svc , SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING , & data .notify );
2266
2394
ok (dr == ERROR_SUCCESS , "NotifyServiceStatusChangeW failed: %u\n" , dr );
2267
2395
2396
+ CloseServiceHandle (svc );
2397
+
2398
+ br = StartServiceA (svc2 , 0 , NULL );
2399
+ ok (br , "StartService failed: %u\n" , GetLastError ());
2400
+
2268
2401
dr = SleepEx (100 , TRUE);
2269
- ok (dr == WAIT_IO_COMPLETION , "APC wasn't called\n" );
2402
+ ok (dr == 0 , "Got wrong SleepEx result: %u\n" , dr );
2403
+ ok (data .was_called == FALSE, "APC should not have been called\n" );
2404
+
2405
+ br = ControlService (svc2 , SERVICE_CONTROL_STOP , & status );
2406
+ ok (br , "ControlService failed: %u\n" , GetLastError ());
2407
+
2408
+ CloseServiceHandle (svc2 );
2270
2409
}
2271
2410
2272
2411
static void test_start_stop (void )
@@ -2352,7 +2491,7 @@ static void test_start_stop(void)
2352
2491
displayname = "Winetest Service" ;
2353
2492
ret = ChangeServiceConfigA (svc_handle , SERVICE_NO_CHANGE , SERVICE_NO_CHANGE , SERVICE_NO_CHANGE , cmd , NULL , NULL , NULL , NULL , NULL , displayname );
2354
2493
ok (ret , "ChangeServiceConfig() failed le=%u\n" , GetLastError ());
2355
- test_servicenotify (svc_handle );
2494
+ test_servicenotify (scm_handle , servicename );
2356
2495
2357
2496
cleanup :
2358
2497
if (svc_handle )
0 commit comments