After this and the related MRs, MS Office 365 finally loads. We see the fancy start screen, and we need modify the options to trust the folder we have our Word documents in. This works for `WINWORD.EXE`, `EXCEL.EXE` and `POWERPNT.exe`. I am able to open an existing file and view it.
This MR is limited to showing the first page or so of the Word document. It freezes with the Wine crash dialog basically the moment right after the screen is rendered. There is no backtrace in the dialog, but winedbg points to `v8jsi.dll` and so does the console output without winedbg. Creating new documents is not achieved by this MR either.
-- v3: sppc: Add stubs for MS Office.
From: Daniel Tang danielzgtg.opensource@gmail.com
--- configure | 1 + configure.ac | 1 + dlls/sppc/Makefile.in | 1 + dlls/sppc/sppc.c | 35 ++++++++++ dlls/sppc/sppc.spec | 4 +- dlls/sppc/tests/Makefile.in | 5 ++ dlls/sppc/tests/sppc.c | 132 ++++++++++++++++++++++++++++++++++++ include/slpublic.h | 4 ++ 8 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 dlls/sppc/tests/Makefile.in create mode 100644 dlls/sppc/tests/sppc.c
diff --git a/configure b/configure index 7618e8b4b15..3a15b028016 100755 --- a/configure +++ b/configure @@ -21585,6 +21585,7 @@ wine_fn_config_makefile dlls/sound.drv16 enable_win16 wine_fn_config_makefile dlls/spoolss enable_spoolss wine_fn_config_makefile dlls/spoolss/tests enable_tests wine_fn_config_makefile dlls/sppc enable_sppc +wine_fn_config_makefile dlls/sppc/tests enable_tests wine_fn_config_makefile dlls/srclient enable_srclient wine_fn_config_makefile dlls/srvcli enable_srvcli wine_fn_config_makefile dlls/sspicli enable_sspicli diff --git a/configure.ac b/configure.ac index 35af177b013..212826a3317 100644 --- a/configure.ac +++ b/configure.ac @@ -2992,6 +2992,7 @@ WINE_CONFIG_MAKEFILE(dlls/sound.drv16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/spoolss) WINE_CONFIG_MAKEFILE(dlls/spoolss/tests) WINE_CONFIG_MAKEFILE(dlls/sppc) +WINE_CONFIG_MAKEFILE(dlls/sppc/tests) WINE_CONFIG_MAKEFILE(dlls/srclient) WINE_CONFIG_MAKEFILE(dlls/srvcli) WINE_CONFIG_MAKEFILE(dlls/sspicli) diff --git a/dlls/sppc/Makefile.in b/dlls/sppc/Makefile.in index 1cd222f6175..d4867a9af76 100644 --- a/dlls/sppc/Makefile.in +++ b/dlls/sppc/Makefile.in @@ -1,4 +1,5 @@ MODULE = sppc.dll +IMPORTLIB = sppc
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/sppc/sppc.c b/dlls/sppc/sppc.c index 8819961d7c6..10672bec4e8 100644 --- a/dlls/sppc/sppc.c +++ b/dlls/sppc/sppc.c @@ -31,6 +31,12 @@
WINE_DEFAULT_DEBUG_CHANNEL(slc);
+static BOOL IsMSOffice(void) +{ + char filename[255]; + return GetModuleFileNameA(NULL, filename, sizeof(filename)) && strstr(filename, "Microsoft Office"); +} + HRESULT WINAPI SLGetLicensingStatusInformation(HSLC handle, const SLID *app, const SLID *product, LPCWSTR name, UINT *count, SL_LICENSING_STATUS **status) { @@ -39,6 +45,16 @@ HRESULT WINAPI SLGetLicensingStatusInformation(HSLC handle, const SLID *app, con return SL_E_RIGHT_NOT_CONSUMED; }
+HRESULT WINAPI SLGetApplicationPolicy(HSLP handle, const WCHAR *name, UINT *type, UINT *count, BYTE **data) +{ + FIXME("(%p %s %p %p %p) stub\n", handle, debugstr_w(name), type, count, data ); + + *count = 0; + *data = (BYTE *)0xdeadbeef; + + return S_OK; +} + HRESULT WINAPI SLOpen(HSLC *handle) { FIXME("(%p) stub\n", handle ); @@ -48,6 +64,16 @@ HRESULT WINAPI SLOpen(HSLC *handle)
*handle = (HSLC)0xdeadbeef;
+ /* + * Microsoft office stops crashing and just says adds to the title a suffix of + * "(Non-Commercial Use) (Unlicensed Product)" if we make the thread sleep forever + */ + if (IsMSOffice()) { + for (;;) { + Sleep(1000); + } + } + return S_OK; }
@@ -58,6 +84,15 @@ HRESULT WINAPI SLClose(HSLC handle) return S_OK; }
+HRESULT WINAPI SLLoadApplicationPolicies(const SLID *app, const SLID *product, DWORD flags, HSLP *result) +{ + FIXME("(%s,%s,%lx,%p) stub\n", wine_dbgstr_guid(app), wine_dbgstr_guid(product), flags, result); + + *result = (HSLP)0xdeadbeef; + + return S_OK; +} + HRESULT WINAPI SLPersistApplicationPolicies(const SLID *app, const SLID *product, DWORD flags) { FIXME("(%s,%s,%lx) stub\n", wine_dbgstr_guid(app), wine_dbgstr_guid(product), flags); diff --git a/dlls/sppc/sppc.spec b/dlls/sppc/sppc.spec index 6926d4d8b6d..e31dc2a9786 100644 --- a/dlls/sppc/sppc.spec +++ b/dlls/sppc/sppc.spec @@ -30,7 +30,7 @@ @ stub SLGenerateOfflineInstallationIdEx @ stub SLGetActiveLicenseInfo @ stub SLGetApplicationInformation -@ stub SLGetApplicationPolicy +@ stdcall SLGetApplicationPolicy(ptr wstr ptr ptr ptr) @ stub SLGetAuthenticationResult @ stub SLGetEncryptedPIDEx @ stub SLGetGenuineInformation @@ -50,7 +50,7 @@ @ stub SLInstallProofOfPurchase @ stub SLInstallProofOfPurchaseEx @ stub SLIsGenuineLocalEx -@ stub SLLoadApplicationPolicies +@ stdcall SLLoadApplicationPolicies(ptr ptr long ptr) @ stdcall SLOpen(ptr) @ stdcall SLPersistApplicationPolicies(ptr ptr long) @ stub SLPersistRTSPayloadOverride diff --git a/dlls/sppc/tests/Makefile.in b/dlls/sppc/tests/Makefile.in new file mode 100644 index 00000000000..456ede1c6d8 --- /dev/null +++ b/dlls/sppc/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = sppc.dll +IMPORTS = sppc + +C_SRCS = \ + sppc.c diff --git a/dlls/sppc/tests/sppc.c b/dlls/sppc/tests/sppc.c new file mode 100644 index 00000000000..6fa5f36344c --- /dev/null +++ b/dlls/sppc/tests/sppc.c @@ -0,0 +1,132 @@ +/* + * Copyright 2023 Daniel Tang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" + +#include "slpublic.h" +#include "slerror.h" + +#include <wine/test.h> + +static const SLID app; +static const SLID product; + +static void test_SLGetLicensingStatusInformation(void) +{ + HSLC handle; + UINT count; + SL_LICENSING_STATUS *status; + HRESULT res; + + handle = (HSLC)0xdeadbeef; + res = SLGetLicensingStatusInformation(handle, &app, &product, L"Name", &count, &status); + ok(res == SL_E_RIGHT_NOT_CONSUMED, "expected SL_E_RIGHT_NOT_CONSUMED, got %08lx\n", res); +} + +static void test_SLGetApplicationPolicy(void) +{ + HSLP handle; + UINT type; + UINT count; + BYTE *data; + HRESULT res; + + handle = (HSLP)0xdeadbeef; + res = SLGetApplicationPolicy(handle, L"Name", &type, &count, &data); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); + ok(count == 0, "expected 0, got %u\n", count); +} + +static void test_SLGetSLIDList(void) +{ + HSLC handle; + UINT count; + SLID *data; + HRESULT res; + + handle = (HSLC)0xdeadbeef; + res = SLGetSLIDList(handle, 0, &app, 0, &count, &data); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); + ok(count == 0, "expected 0, got %u\n", count); +} + +static void test_SLInstallLicense(void) +{ + HSLC handle; + SLID file; + HRESULT res; + + handle = (HSLC)0xdeadbeef; + res = SLInstallLicense(handle, 1, NULL, &file); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); +} + +static void test_SLOpen(void) +{ + HSLC handle; + HRESULT res; + + res = SLOpen(&handle); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); + ok(handle == (HSLC)0xdeadbeef, "expected handle = 0xdeadbeef, got %p\n", handle); +} + +static void test_SLClose(void) +{ + HSLC handle; + HRESULT res; + + handle = (HSLC)0xdeadbeef; + res = SLClose(handle); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); +} + +static void test_SLLoadApplicationPolicies(void) +{ + HSLP handle; + HRESULT res; + + res = SLLoadApplicationPolicies(&app, &product, 0, &handle); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); + ok(handle == (HSLP)0xdeadbeef, "expected handle = 0xdeadbeef, got %p\n", handle); +} + +static void test_SLPersistApplicationPolicies(void) +{ + HRESULT res; + + res = SLPersistApplicationPolicies(&app, &product, 0); + ok(res == S_OK, "expected S_OK, got %08lx\n", res); +} + + +START_TEST(sppc) +{ + test_SLGetLicensingStatusInformation(); + test_SLGetApplicationPolicy(); + test_SLGetSLIDList(); + test_SLInstallLicense(); + test_SLOpen(); + test_SLClose(); + test_SLLoadApplicationPolicies(); + test_SLPersistApplicationPolicies(); +} diff --git a/include/slpublic.h b/include/slpublic.h index 3f3a39274d1..e945b2fe841 100644 --- a/include/slpublic.h +++ b/include/slpublic.h @@ -33,6 +33,8 @@ typedef GUID SLID;
typedef PVOID HSLC;
+typedef PVOID HSLP; + typedef enum _tagSLDATATYPE { SL_DATA_NONE = REG_NONE, @@ -63,9 +65,11 @@ typedef struct _tagSL_LICENSING_STATUS } SL_LICENSING_STATUS;
SLCAPI HRESULT WINAPI SLGetLicensingStatusInformation(HSLC, const SLID*, const SLID*, LPCWSTR, UINT*, SL_LICENSING_STATUS**); +SLCAPI HRESULT WINAPI SLGetApplicationPolicy(HSLP, const WCHAR *, UINT *, UINT *, BYTE **); SLCAPI HRESULT WINAPI SLGetWindowsInformation(LPCWSTR, SLDATATYPE*, UINT*, LPBYTE*); SLCAPI HRESULT WINAPI SLGetWindowsInformationDWORD(LPCWSTR, LPDWORD); SLCAPI HRESULT WINAPI SLOpen(HSLC*); +SLCAPI HRESULT WINAPI SLLoadApplicationPolicies(const SLID *, const SLID *, DWORD, HSLP *);
#ifdef __cplusplus }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=130520
Your paranoid android.
=== build (build log) ===
/home/winetest/tools/testbot/var/wine-exe32/../wine/dlls/sppc/tests/sppc.c:67: undefined reference to `SLGetSLIDList' /home/winetest/tools/testbot/var/wine-exe32/../wine/dlls/sppc/tests/sppc.c:79: undefined reference to `SLInstallLicense' /home/winetest/tools/testbot/var/wine-exe32/../wine/dlls/sppc/tests/sppc.c:99: undefined reference to `SLClose' /home/winetest/tools/testbot/var/wine-exe32/../wine/dlls/sppc/tests/sppc.c:117: undefined reference to `SLPersistApplicationPolicies' collect2: error: ld returned 1 exit status Makefile:157367: recipe for target 'dlls/sppc/tests/i386-windows/sppc_test.exe' failed Task: The exe32 Wine build failed
=== debian11 (build log) ===
/home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sppc/tests/sppc.c:67: undefined reference to `SLGetSLIDList' /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sppc/tests/sppc.c:79: undefined reference to `SLInstallLicense' /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sppc/tests/sppc.c:99: undefined reference to `SLClose' /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sppc/tests/sppc.c:117: undefined reference to `SLPersistApplicationPolicies' collect2: error: ld returned 1 exit status Task: The win32 Wine build failed
=== debian11b (build log) ===
/home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/sppc/tests/sppc.c:67: undefined reference to `SLGetSLIDList' /home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/sppc/tests/sppc.c:79: undefined reference to `SLInstallLicense' collect2: error: ld returned 1 exit status Task: The wow64 Wine build failed
I wrote the tests like you asked for. I also minimized the diff so that half of the application-specific hacks are identified as no longer needed gone.
Before the first published version, I had a version where I implemented many more functions. It didn't work and the diff was 3x as long. It used to be implemented in a more regular way, but it might not have been legal to publish. It involved returning `S_OK` from `SLGetLicensingStatusInformation` and `SLConsumeRight`, which may violate DMCA anti-circumvention laws. On the other hand, this MR does not bypass DRM but merely slows them down, and since threads are nondeterministic, the software vendor must have intended to allow this for slow computers. Compared to my current version, the unpublished version contained questionable uses of `LocalAlloc`. Furthermore I don't really understand the `sppc` module beyond what I need to get my software working.
For making it less application-specific, would you have removed the `IsMSOffice()` check altogether? I added it to reduce the risk of regressions, but now I realize something else. I've never seen a 3rd-party application rely on `sppc` for license checks, as they all use their own. This means `sppc` is already related to Microsoft products, and Microsoft products tend to be around Microsoft Office.
Thanks for working on this @danielzgtg! I started going down a similar path recently but didn't get nearly as far as you have.
Let me answer a couple of your questions: Yes, IsMSOffice has got to go. Never returning from SLOpen is also a little weird but could be fine. I'd prefer to find a way to make Office work without sleeping forever in SLOpen, but I would argue that going to sleep in SLOpen is better than not implementing SLOpen at all, and I hope that other Wine developers feel similarly.
Is it strictly necessary to set the output pointers to 0xdeadbeef? Would Office work if you just set them to NULL, or if you don't set them at all?
It's good that you added tests, but please only include tests for the functions that you have implemented. The tests need to be able to compile, run, and pass on both Windows and Wine. If a particular test passes on Windows but not on Wine, mark it `todo_wine` and the tests will still be considered to pass as a whole.
I hope that helps!