mirror of https://github.com/AxioDL/metaforce.git
Windows refactors
This commit is contained in:
parent
199b8e7c32
commit
e4f53eaa7c
|
@ -9,6 +9,11 @@
|
|||
#include <LogVisor/LogVisor.hpp>
|
||||
#include "BlenderConnection.hpp"
|
||||
|
||||
#if _WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
namespace HECL
|
||||
{
|
||||
|
||||
|
@ -17,8 +22,6 @@ BlenderConnection* SharedBlenderConnection = nullptr;
|
|||
|
||||
#ifdef __APPLE__
|
||||
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
|
||||
#elif _WIN32
|
||||
#define DEFAULT_BLENDER_BIN _S("%ProgramFiles%\\Blender Foundation\\Blender\\blender.exe")
|
||||
#else
|
||||
#define DEFAULT_BLENDER_BIN "blender"
|
||||
#endif
|
||||
|
@ -26,6 +29,19 @@ BlenderConnection* SharedBlenderConnection = nullptr;
|
|||
extern "C" uint8_t HECL_BLENDERSHELL[];
|
||||
extern "C" size_t HECL_BLENDERSHELL_SZ;
|
||||
|
||||
extern "C" uint8_t HECL_ADDON[];
|
||||
extern "C" size_t HECL_ADDON_SZ;
|
||||
|
||||
static bool InstallAddon(const SystemChar* path)
|
||||
{
|
||||
FILE* fp = HECL::Fopen(path, _S("wb"));
|
||||
if (!fp)
|
||||
BlenderLog.report(LogVisor::FatalError, _S("Unable to install blender addon at '%s'"), path);
|
||||
fwrite(HECL_ADDON, 1, HECL_ADDON_SZ, fp);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
|
||||
{
|
||||
size_t readBytes = 0;
|
||||
|
@ -37,15 +53,9 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
|
|||
*(buf-1) = '\0';
|
||||
return bufSz - 1;
|
||||
}
|
||||
#if _WIN32
|
||||
DWORD ret = 0;
|
||||
if (!ReadFile(m_readpipe[0], buf, 1, &ret, NULL))
|
||||
goto err;
|
||||
#else
|
||||
ssize_t ret = read(m_readpipe[0], buf, 1);
|
||||
int ret = read(m_readpipe[0], buf, 1);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
#endif
|
||||
else if (ret == 1)
|
||||
{
|
||||
if (*buf == '\n')
|
||||
|
@ -69,21 +79,13 @@ err:
|
|||
|
||||
size_t BlenderConnection::_writeLine(const char* buf)
|
||||
{
|
||||
#if _WIN32
|
||||
DWORD ret = 0;
|
||||
if (!WriteFile(m_writepipe[1], buf, strlen(buf), &ret, NULL))
|
||||
goto err;
|
||||
if (!WriteFile(m_writepipe[1], "\n", 1, NULL, NULL))
|
||||
goto err;
|
||||
#else
|
||||
ssize_t ret, nlerr;
|
||||
int ret, nlerr;
|
||||
ret = write(m_writepipe[1], buf, strlen(buf));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
nlerr = write(m_writepipe[1], "\n", 1);
|
||||
if (nlerr < 0)
|
||||
goto err;
|
||||
#endif
|
||||
return (size_t)ret;
|
||||
err:
|
||||
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
||||
|
@ -92,15 +94,9 @@ err:
|
|||
|
||||
size_t BlenderConnection::_readBuf(char* buf, size_t len)
|
||||
{
|
||||
#if _WIN32
|
||||
DWORD ret = 0;
|
||||
if (!ReadFile(m_readpipe[0], buf, len, &ret, NULL))
|
||||
goto err;
|
||||
#else
|
||||
ssize_t ret = read(m_readpipe[0], buf, len);
|
||||
int ret = read(m_readpipe[0], buf, len);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
#endif
|
||||
return ret;
|
||||
err:
|
||||
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
||||
|
@ -109,15 +105,9 @@ err:
|
|||
|
||||
size_t BlenderConnection::_writeBuf(const char* buf, size_t len)
|
||||
{
|
||||
#if _WIN32
|
||||
DWORD ret = 0;
|
||||
if (!WriteFile(m_writepipe[1], buf, len, &ret, NULL))
|
||||
goto err;
|
||||
#else
|
||||
ssize_t ret = write(m_writepipe[1], buf, len);
|
||||
int ret = write(m_writepipe[1], buf, len);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
#endif
|
||||
return ret;
|
||||
err:
|
||||
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
||||
|
@ -126,13 +116,8 @@ err:
|
|||
|
||||
void BlenderConnection::_closePipe()
|
||||
{
|
||||
#if _WIN32
|
||||
CloseHandle(m_readpipe[0]);
|
||||
CloseHandle(m_writepipe[1]);
|
||||
#else
|
||||
close(m_readpipe[0]);
|
||||
close(m_writepipe[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
BlenderConnection::BlenderConnection(bool silenceBlender)
|
||||
|
@ -155,156 +140,181 @@ BlenderConnection::BlenderConnection(bool silenceBlender)
|
|||
fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp);
|
||||
fclose(fp);
|
||||
|
||||
/* Construct communication pipes */
|
||||
#if _WIN32
|
||||
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
|
||||
CreatePipe(&m_readpipe[0], &m_readpipe[1], &sattrs, 0);
|
||||
CreatePipe(&m_writepipe[0], &m_writepipe[1], &sattrs, 0);
|
||||
#else
|
||||
pipe(m_readpipe);
|
||||
pipe(m_writepipe);
|
||||
#endif
|
||||
HECL::SystemString blenderAddonPath(TMPDIR);
|
||||
blenderAddonPath += _S("/hecl_blenderaddon.zip");
|
||||
|
||||
/* User-specified blender path */
|
||||
#if _WIN32
|
||||
wchar_t BLENDER_BIN_BUF[2048];
|
||||
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
|
||||
#else
|
||||
char* blenderBin = getenv("BLENDER_BIN");
|
||||
#endif
|
||||
|
||||
/* Child process of blender */
|
||||
#if _WIN32
|
||||
if (!blenderBin)
|
||||
int installAttempt = 0;
|
||||
while (true)
|
||||
{
|
||||
/* Environment not set; use registry */
|
||||
HKEY blenderKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\BlenderFoundation", 0, KEY_READ, &blenderKey) == ERROR_SUCCESS)
|
||||
|
||||
/* Construct communication pipes */
|
||||
#if _WIN32
|
||||
_pipe(m_readpipe, 2048, _O_BINARY);
|
||||
_pipe(m_writepipe, 2048, _O_BINARY);
|
||||
HANDLE writehandle = HANDLE(_get_osfhandle(m_writepipe[0]));
|
||||
SetHandleInformation(writehandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
|
||||
HANDLE readhandle = HANDLE(_get_osfhandle(m_readpipe[1]));
|
||||
SetHandleInformation(readhandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
|
||||
#else
|
||||
pipe(m_readpipe);
|
||||
pipe(m_writepipe);
|
||||
#endif
|
||||
|
||||
/* User-specified blender path */
|
||||
#if _WIN32
|
||||
wchar_t BLENDER_BIN_BUF[2048];
|
||||
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
|
||||
#else
|
||||
char* blenderBin = getenv("BLENDER_BIN");
|
||||
#endif
|
||||
|
||||
/* Child process of blender */
|
||||
#if _WIN32
|
||||
if (!blenderBin)
|
||||
{
|
||||
DWORD bufSz = sizeof(BLENDER_BIN_BUF);
|
||||
if (RegGetValueW(blenderKey, NULL, L"Install_Dir", REG_SZ, NULL, BLENDER_BIN_BUF, &bufSz) == ERROR_SUCCESS)
|
||||
{
|
||||
wcscat_s(BLENDER_BIN_BUF, 2048, L"\\blender.exe");
|
||||
blenderBin = BLENDER_BIN_BUF;
|
||||
}
|
||||
RegCloseKey(blenderKey);
|
||||
/* Environment not set; use default */
|
||||
wchar_t progFiles[256];
|
||||
if (!GetEnvironmentVariableW(L"ProgramFiles", progFiles, 256))
|
||||
BlenderLog.report(LogVisor::FatalError, L"unable to determine 'Program Files' path");
|
||||
_snwprintf(BLENDER_BIN_BUF, 2048, L"%s\\Blender Foundation\\Blender\\blender.exe", progFiles);
|
||||
blenderBin = BLENDER_BIN_BUF;
|
||||
}
|
||||
}
|
||||
if (!blenderBin)
|
||||
{
|
||||
Log.report(LogVisor::FatalError, "unable to find blender");
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t cmdLine[2048];
|
||||
_snwprintf(cmdLine, 2048, L" --background -P shellscript.py -- %08X %08X",
|
||||
(uint32_t)m_writepipe[0], (uint32_t)m_readpipe[1]);
|
||||
|
||||
STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
|
||||
HANDLE nulHandle = NULL;
|
||||
if (silenceBlender)
|
||||
{
|
||||
nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
sinfo.hStdError = nulHandle;
|
||||
sinfo.hStdOutput = nulHandle;
|
||||
sinfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION pinfo;
|
||||
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
|
||||
Log.report(LogVisor::FatalError, "unable to launch blender");
|
||||
|
||||
CloseHandle(m_writepipe[1]);
|
||||
CloseHandle(m_readpipe[0]);
|
||||
|
||||
if (nulHandle)
|
||||
CloseHandle(nulHandle);
|
||||
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (!pid)
|
||||
{
|
||||
close(m_writepipe[1]);
|
||||
close(m_readpipe[0]);
|
||||
wchar_t cmdLine[2048];
|
||||
if (installAttempt == 1)
|
||||
_snwprintf(cmdLine, 2048, L" --background -P \"%s\" -- %d %d \"%s\"",
|
||||
blenderShellPath.c_str(), int(writehandle), int(readhandle), blenderAddonPath.c_str());
|
||||
else
|
||||
_snwprintf(cmdLine, 2048, L" --background -P \"%s\" -- %d %d",
|
||||
blenderShellPath.c_str(), int(writehandle), int(readhandle));
|
||||
|
||||
STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
|
||||
HANDLE nulHandle = NULL;
|
||||
if (silenceBlender)
|
||||
{
|
||||
int devNull = open("/dev/null", O_WRONLY);
|
||||
dup2(devNull, STDOUT_FILENO);
|
||||
dup2(devNull, STDERR_FILENO);
|
||||
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
|
||||
nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
sinfo.hStdError = nulHandle;
|
||||
sinfo.hStdOutput = nulHandle;
|
||||
sinfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
}
|
||||
|
||||
char errbuf[256];
|
||||
char readfds[32];
|
||||
snprintf(readfds, 32, "%d", m_writepipe[0]);
|
||||
char writefds[32];
|
||||
snprintf(writefds, 32, "%d", m_readpipe[1]);
|
||||
|
||||
/* Try user-specified blender first */
|
||||
if (blenderBin)
|
||||
PROCESS_INFORMATION pinfo;
|
||||
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
|
||||
{
|
||||
execlp(blenderBin, blenderBin,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, NULL);
|
||||
LPWSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
|
||||
BlenderLog.report(LogVisor::FatalError, L"unable to launch blender from %s: %s", blenderBin, messageBuffer);
|
||||
}
|
||||
|
||||
close(m_writepipe[0]);
|
||||
close(m_readpipe[1]);
|
||||
|
||||
if (nulHandle)
|
||||
CloseHandle(nulHandle);
|
||||
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (!pid)
|
||||
{
|
||||
close(m_writepipe[1]);
|
||||
close(m_readpipe[0]);
|
||||
|
||||
if (silenceBlender)
|
||||
{
|
||||
int devNull = open("/dev/null", O_WRONLY);
|
||||
dup2(devNull, STDOUT_FILENO);
|
||||
dup2(devNull, STDERR_FILENO);
|
||||
}
|
||||
|
||||
char errbuf[256];
|
||||
char readfds[32];
|
||||
snprintf(readfds, 32, "%d", m_writepipe[0]);
|
||||
char writefds[32];
|
||||
snprintf(writefds, 32, "%d", m_readpipe[1]);
|
||||
|
||||
/* Try user-specified blender first */
|
||||
if (blenderBin)
|
||||
{
|
||||
if (installAttempt == 1)
|
||||
execlp(blenderBin, blenderBin,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, blenderAddonPath.c_str(), NULL);
|
||||
else
|
||||
execlp(blenderBin, blenderBin,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, NULL);
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
|
||||
write(m_writepipe[1], errbuf, strlen(errbuf));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise default blender */
|
||||
if (installAttempt == 1)
|
||||
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, blenderAddonPath.c_str(), NULL);
|
||||
else
|
||||
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, NULL);
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
|
||||
write(m_writepipe[1], errbuf, strlen(errbuf));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise default blender */
|
||||
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
|
||||
"--background", "-P", blenderShellPath.c_str(),
|
||||
"--", readfds, writefds, NULL);
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
|
||||
write(m_writepipe[1], errbuf, strlen(errbuf));
|
||||
/* Unable to find blender */
|
||||
write(m_writepipe[1], "NOBLENDER\n", 10);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Unable to find blender */
|
||||
write(m_writepipe[1], "NOBLENDER\n", 10);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
close(m_writepipe[0]);
|
||||
close(m_readpipe[1]);
|
||||
m_blenderProc = pid;
|
||||
close(m_writepipe[0]);
|
||||
close(m_readpipe[1]);
|
||||
m_blenderProc = pid;
|
||||
#endif
|
||||
|
||||
/* Handle first response */
|
||||
char lineBuf[256];
|
||||
_readLine(lineBuf, sizeof(lineBuf));
|
||||
if (!strcmp(lineBuf, "NOLAUNCH"))
|
||||
{
|
||||
_closePipe();
|
||||
BlenderLog.report(LogVisor::FatalError, "Unable to launch blender");
|
||||
}
|
||||
else if (!strcmp(lineBuf, "NOBLENDER"))
|
||||
{
|
||||
_closePipe();
|
||||
if (blenderBin)
|
||||
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s' or '%s'"),
|
||||
blenderBin, DEFAULT_BLENDER_BIN);
|
||||
else
|
||||
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"),
|
||||
DEFAULT_BLENDER_BIN);
|
||||
}
|
||||
else if (!strcmp(lineBuf, "NOADDON"))
|
||||
{
|
||||
_closePipe();
|
||||
BlenderLog.report(LogVisor::FatalError, "HECL addon not installed within blender");
|
||||
}
|
||||
else if (strcmp(lineBuf, "READY"))
|
||||
{
|
||||
_closePipe();
|
||||
BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf);
|
||||
}
|
||||
_writeLine("ACK");
|
||||
/* Handle first response */
|
||||
char lineBuf[256];
|
||||
_readLine(lineBuf, sizeof(lineBuf));
|
||||
if (!strcmp(lineBuf, "NOLAUNCH"))
|
||||
{
|
||||
_closePipe();
|
||||
BlenderLog.report(LogVisor::FatalError, "Unable to launch blender");
|
||||
}
|
||||
else if (!strcmp(lineBuf, "NOBLENDER"))
|
||||
{
|
||||
_closePipe();
|
||||
if (blenderBin)
|
||||
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s' or '%s'"),
|
||||
blenderBin, DEFAULT_BLENDER_BIN);
|
||||
else
|
||||
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"),
|
||||
DEFAULT_BLENDER_BIN);
|
||||
}
|
||||
else if (!strcmp(lineBuf, "NOADDON"))
|
||||
{
|
||||
_closePipe();
|
||||
InstallAddon(blenderAddonPath.c_str());
|
||||
++installAttempt;
|
||||
if (installAttempt >= 2)
|
||||
BlenderLog.report(LogVisor::FatalError, "unable to install blender addon using '%s'", blenderAddonPath.c_str());
|
||||
continue;
|
||||
}
|
||||
else if (strcmp(lineBuf, "READY"))
|
||||
{
|
||||
_closePipe();
|
||||
BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf);
|
||||
}
|
||||
_writeLine("ACK");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BlenderConnection::~BlenderConnection()
|
||||
|
@ -326,7 +336,7 @@ bool BlenderConnection::createBlend(const SystemString& path)
|
|||
_readLine(lineBuf, sizeof(lineBuf));
|
||||
if (!strcmp(lineBuf, "FINISHED"))
|
||||
{
|
||||
m_loadedBlend = pathView.str();
|
||||
m_loadedBlend = path;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -346,7 +356,7 @@ bool BlenderConnection::openBlend(const SystemString& path)
|
|||
_readLine(lineBuf, sizeof(lineBuf));
|
||||
if (!strcmp(lineBuf, "FINISHED"))
|
||||
{
|
||||
m_loadedBlend = pathView.str();
|
||||
m_loadedBlend = path;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -373,12 +383,12 @@ void BlenderConnection::deleteBlend()
|
|||
if (m_loadedBlend.size())
|
||||
{
|
||||
HECL::Unlink(m_loadedBlend.c_str());
|
||||
BlenderLog.report(LogVisor::Info, "Deleted '%s'", m_loadedBlend.c_str());
|
||||
BlenderLog.report(LogVisor::Info, _S("Deleted '%s'"), m_loadedBlend.c_str());
|
||||
m_loadedBlend.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderConnection::PyOutStream::linkBlend(const SystemString& target, const std::string& objName,
|
||||
void BlenderConnection::PyOutStream::linkBlend(const std::string& target, const std::string& objName,
|
||||
bool link)
|
||||
{
|
||||
format("if '%s' not in bpy.data.scenes:\n"
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#define BLENDERCONNECTION_HPP
|
||||
|
||||
#if _WIN32
|
||||
#define _WIN32_LEAN_AND_MEAN 1
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
@ -23,17 +25,15 @@ extern class BlenderConnection* SharedBlenderConnection;
|
|||
|
||||
class BlenderConnection
|
||||
{
|
||||
bool m_lock;
|
||||
bool m_lock = false;
|
||||
#if _WIN32
|
||||
HANDLE m_blenderProc;
|
||||
HANDLE m_readpipe[2];
|
||||
HANDLE m_writepipe[2];
|
||||
#else
|
||||
pid_t m_blenderProc;
|
||||
#endif
|
||||
int m_readpipe[2];
|
||||
int m_writepipe[2];
|
||||
#endif
|
||||
std::string m_loadedBlend;
|
||||
SystemString m_loadedBlend;
|
||||
size_t _readLine(char* buf, size_t bufSz);
|
||||
size_t _writeLine(const char* buf);
|
||||
size_t _readBuf(char* buf, size_t len);
|
||||
|
@ -75,11 +75,12 @@ public:
|
|||
{
|
||||
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
|
||||
BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream writing");
|
||||
if (ch != traits_type::eof() && ch != '\n')
|
||||
if (ch != traits_type::eof() && ch != '\n' && ch != '\0')
|
||||
{
|
||||
m_lineBuf += char_type(ch);
|
||||
return ch;
|
||||
}
|
||||
//printf("FLUSHING %s\n", m_lineBuf.c_str());
|
||||
m_parent.m_parent->_writeLine(m_lineBuf.c_str());
|
||||
char readBuf[16];
|
||||
m_parent.m_parent->_readLine(readBuf, 16);
|
||||
|
@ -109,7 +110,8 @@ public:
|
|||
public:
|
||||
PyOutStream(const PyOutStream& other) = delete;
|
||||
PyOutStream(PyOutStream&& other)
|
||||
: m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf))
|
||||
: m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)),
|
||||
std::ostream(&m_sbuf)
|
||||
{other.m_parent = nullptr;}
|
||||
~PyOutStream() {close();}
|
||||
void close()
|
||||
|
@ -131,13 +133,19 @@ public:
|
|||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char* result = nullptr;
|
||||
#ifdef _WIN32
|
||||
int length = _vscprintf(fmt, ap);
|
||||
result = (char*)malloc(length);
|
||||
vsnprintf(result, length, fmt, ap);
|
||||
#else
|
||||
int length = vasprintf(&result, fmt, ap);
|
||||
#endif
|
||||
va_end(ap);
|
||||
if (length > 0)
|
||||
this->write(result, length);
|
||||
free(result);
|
||||
va_end(ap);
|
||||
}
|
||||
void linkBlend(const SystemString& target, const std::string& objName, bool link=true);
|
||||
void linkBlend(const std::string& target, const std::string& objName, bool link=true);
|
||||
};
|
||||
inline PyOutStream beginPythonOut(bool deleteOnError=false)
|
||||
{
|
||||
|
|
|
@ -13,9 +13,20 @@ list(APPEND PY_SOURCES
|
|||
|
||||
bintoc(hecl_blendershell.c hecl_blendershell.py HECL_BLENDERSHELL)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip)
|
||||
message("-- Generating addon package")
|
||||
execute_process(COMMAND python zip_package.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip)
|
||||
message(FATAL_ERROR "Unable to generate ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip; is python installed?")
|
||||
endif()
|
||||
endif()
|
||||
bintoc(hecl_addon.c hecl.zip HECL_ADDON)
|
||||
|
||||
add_library(HECLBlender
|
||||
BlenderConnection.cpp
|
||||
BlenderConnection.hpp
|
||||
hecl_blendershell.py
|
||||
hecl_blendershell.c
|
||||
hecl_addon.c
|
||||
${PY_SOURCES})
|
||||
|
|
|
@ -9,6 +9,10 @@ if '--' not in sys.argv:
|
|||
args = sys.argv[sys.argv.index('--')+1:]
|
||||
readfd = int(args[0])
|
||||
writefd = int(args[1])
|
||||
if sys.platform == "win32":
|
||||
import msvcrt
|
||||
readfd = msvcrt.open_osfhandle(readfd, os.O_RDONLY | os.O_BINARY)
|
||||
writefd = msvcrt.open_osfhandle(writefd, os.O_WRONLY | os.O_BINARY)
|
||||
|
||||
def readpipeline():
|
||||
retval = bytearray()
|
||||
|
@ -29,14 +33,23 @@ def quitblender():
|
|||
writepipeline(b'QUITTING')
|
||||
bpy.ops.wm.quit_blender()
|
||||
|
||||
# Check that HECL addon is installed/enabled
|
||||
if 'hecl' not in bpy.context.user_preferences.addons:
|
||||
if 'FINISHED' not in bpy.ops.wm.addon_enable(module='hecl'):
|
||||
writepipeline(b'NOADDON')
|
||||
bpy.ops.wm.quit_blender()
|
||||
# If there's a third argument, use it as the .zip path containing the addon
|
||||
if len(args) >= 3:
|
||||
bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[2])
|
||||
bpy.ops.wm.addon_refresh()
|
||||
|
||||
# Make addon available to commands
|
||||
import hecl
|
||||
if bpy.context.user_preferences.addons.find('hecl') == -1:
|
||||
try:
|
||||
bpy.ops.wm.addon_enable(module='hecl')
|
||||
bpy.ops.wm.save_userpref()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
import hecl
|
||||
except:
|
||||
writepipeline(b'NOADDON')
|
||||
bpy.ops.wm.quit_blender()
|
||||
|
||||
# Intro handshake
|
||||
writepipeline(b'READY')
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import sys, os
|
||||
import zipfile
|
||||
|
||||
def zipdir(path, ziph):
|
||||
# ziph is zipfile handle
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
ziph.write(os.path.join(root, file))
|
||||
|
||||
package_path = 'hecl'
|
||||
target_zip = 'hecl.zip'
|
||||
|
||||
zf = zipfile.ZipFile(target_zip, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||
print('GENERATING', target_zip)
|
||||
if os.path.isdir(package_path):
|
||||
zipdir(package_path, zf)
|
||||
|
||||
zf.close()
|
|
@ -1,7 +1,3 @@
|
|||
if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /INCLUDE:HECLDataSpecs")
|
||||
endif()
|
||||
|
||||
add_executable(hecl main.cpp
|
||||
ToolBase.hpp
|
||||
ToolPackage.hpp
|
||||
|
@ -24,8 +20,12 @@ list(APPEND DATA_SPEC_LIBS
|
|||
DNAMP1
|
||||
DNACommon)
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND PLAT_LIBS pthread)
|
||||
endif()
|
||||
|
||||
target_link_libraries(hecl
|
||||
${DATA_SPEC_LIBS}
|
||||
HECLDatabase HECLBlender HECLCommon AthenaCore NOD
|
||||
LogVisor AthenaLibYaml png squish blowfish z lzo2 pthread
|
||||
LogVisor AthenaLibYaml png squish blowfish z lzo2 ${PLAT_LIBS}
|
||||
)
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory");
|
||||
|
||||
size_t ErrorRef = LogVisor::ErrorCount;
|
||||
HECL::SystemString rootDir = info.cwd + '/' + baseFile;
|
||||
HECL::SystemString rootDir = info.cwd + _S('/') + baseFile;
|
||||
HECL::ProjectRootPath newProjRoot(rootDir);
|
||||
newProjRoot.makeDir();
|
||||
m_fallbackProj.reset(new HECL::Database::Project(newProjRoot));
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#if _WIN32
|
||||
#define WIN_PAUSE 1
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
#include <clocale>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -89,6 +91,12 @@ int wmain(int argc, const wchar_t** argv)
|
|||
int main(int argc, const char** argv)
|
||||
#endif
|
||||
{
|
||||
#if _WIN32
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
#else
|
||||
std::setlocale(LC_ALL, "en-US.UTF-8");
|
||||
#endif
|
||||
|
||||
/* Xterm check */
|
||||
const char* term = getenv("TERM");
|
||||
if (term && !strncmp(term, "xterm", 5))
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 66cb6c982e8824fa887807bf8ba42376b301478b
|
||||
Subproject commit 488a0100e2e6044361a494a280c7f1a28911762e
|
|
@ -1,5 +1,5 @@
|
|||
add_subdirectory(libpng)
|
||||
add_subdirectory(libSquish)
|
||||
add_subdirectory(blowfish)
|
||||
add_subdirectory(LogVisor)
|
||||
add_subdirectory(Athena EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(libpng)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 154b84413050cdb03f281c42861b9af6eaef7d14
|
||||
Subproject commit 828db515ba316903c367b9dc7acf9d0bec0969e7
|
|
@ -10,11 +10,12 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define _WIN32_LEAN_AND_MEAN 1
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <wchar.h>
|
||||
#include "winsupport.h"
|
||||
#define snprintf _snprintf
|
||||
#include "winsupport.hpp"
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
@ -29,8 +30,10 @@
|
|||
#include "../extern/blowfish/blowfish.h"
|
||||
|
||||
/* Handy MIN/MAX macros */
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
namespace HECL
|
||||
{
|
||||
|
@ -135,11 +138,11 @@ static inline void Unlink(const SystemChar* file)
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void MakeDir(const SystemChar* dir)
|
||||
static inline void MakeDir(const char* dir)
|
||||
{
|
||||
#if _WIN32
|
||||
HRESULT err;
|
||||
if (!CreateDirectory(dir, NULL))
|
||||
if (!CreateDirectoryA(dir, NULL))
|
||||
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
||||
LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir);
|
||||
#else
|
||||
|
@ -149,11 +152,29 @@ static inline void MakeDir(const SystemChar* dir)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
static inline void MakeDir(const wchar_t* dir)
|
||||
{
|
||||
HRESULT err;
|
||||
if (!CreateDirectoryW(dir, NULL))
|
||||
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
||||
LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void MakeLink(const SystemChar* target, const SystemChar* linkPath)
|
||||
{
|
||||
#if _WIN32
|
||||
HRESULT res = CreateShellLink(target, linkPath, _S("HECL Link")); /* :(( */
|
||||
if (!SUCCEEDED(res))
|
||||
{
|
||||
LPWSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); /* :((( */
|
||||
LogModule.report(LogVisor::FatalError, _S("MakeLink(%s, %s): %s"), target, linkPath, messageBuffer);
|
||||
}
|
||||
#else
|
||||
if (symlink(target, linkPath))
|
||||
if (symlink(target, linkPath)) /* :) */
|
||||
if (errno != EEXIST)
|
||||
LogModule.report(LogVisor::FatalError, "MakeLink(%s, %s): %s", target, linkPath, strerror(errno));
|
||||
#endif
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef _HECL_WINSUPPORT_H_
|
||||
#define _HECL_WINSUPPORT_H_
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HECL_WINSUPPORT_H_
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _HECL_WINSUPPORT_H_
|
||||
#define _HECL_WINSUPPORT_H_
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include "windows.h"
|
||||
|
||||
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
|
||||
HRESULT CreateShellLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszDesc);
|
||||
HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize);
|
||||
|
||||
#endif // _HECL_WINSUPPORT_H_
|
|
@ -4,7 +4,7 @@ add_subdirectory(Frontend)
|
|||
add_subdirectory(Runtime)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND PLAT_SRCS winsupport.c ../include/HECL/winsupport.h)
|
||||
list(APPEND PLAT_SRCS winsupport.cpp ../include/HECL/winsupport.hpp)
|
||||
endif()
|
||||
|
||||
add_library(HECLCommon
|
||||
|
|
|
@ -5,29 +5,45 @@ namespace HECL
|
|||
|
||||
std::string WideToUTF8(const std::wstring& src)
|
||||
{
|
||||
#if _WIN32
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, src.c_str(), src.size(), nullptr, 0, nullptr, nullptr);
|
||||
std::string retval(len, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, src.c_str(), src.size(), &retval[0], len, nullptr, nullptr);
|
||||
return retval;
|
||||
#else
|
||||
std::string retval;
|
||||
retval.reserve(src.length());
|
||||
std::mbstate_t state = {};
|
||||
for (wchar_t ch : src)
|
||||
{
|
||||
char mb[4];
|
||||
int c = std::wctomb(mb, ch);
|
||||
char mb[MB_LEN_MAX];
|
||||
int c = std::wcrtomb(mb, ch, &state);
|
||||
retval.append(mb, c);
|
||||
}
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::wstring UTF8ToWide(const std::string& src)
|
||||
{
|
||||
#if _WIN32
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), src.size(), nullptr, 0);
|
||||
std::wstring retval(len, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, src.c_str(), src.size(), &retval[0], len);
|
||||
return retval;
|
||||
#else
|
||||
std::wstring retval;
|
||||
retval.reserve(src.length());
|
||||
const char* buf = src.c_str();
|
||||
std::mbstate_t state = {};
|
||||
while (*buf)
|
||||
{
|
||||
wchar_t wc;
|
||||
buf += std::mbtowc(&wc, buf, MB_CUR_MAX);
|
||||
buf += std::mbrtowc(&wc, buf, MB_LEN_MAX, &state);
|
||||
retval += wc;
|
||||
}
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The memmem() function finds the start of the first occurrence of the
|
||||
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
|
||||
* length 'hlen'.
|
||||
*
|
||||
* The return value is a pointer to the beginning of the sub-string, or
|
||||
* NULL if the substring is not found.
|
||||
*/
|
||||
void *memmem(const uint8_t *haystack, size_t hlen, const void *needle, size_t nlen)
|
||||
{
|
||||
int needle_first;
|
||||
const uint8_t *p = haystack;
|
||||
size_t plen = hlen;
|
||||
|
||||
if (!nlen)
|
||||
return NULL;
|
||||
|
||||
needle_first = *(unsigned char *)needle;
|
||||
|
||||
while (plen >= nlen && (p = memchr(p, needle_first, plen - nlen + 1)))
|
||||
{
|
||||
if (!memcmp(p, needle, nlen))
|
||||
return (void *)p;
|
||||
|
||||
p++;
|
||||
plen = hlen - (p - haystack);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "HECL/winsupport.hpp"
|
||||
|
||||
/*
|
||||
* The memmem() function finds the start of the first occurrence of the
|
||||
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
|
||||
* length 'hlen'.
|
||||
*
|
||||
* The return value is a pointer to the beginning of the sub-string, or
|
||||
* NULL if the substring is not found.
|
||||
*/
|
||||
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
|
||||
{
|
||||
int needle_first;
|
||||
const uint8_t *p = static_cast<const uint8_t*>(haystack);
|
||||
size_t plen = hlen;
|
||||
|
||||
if (!nlen)
|
||||
return NULL;
|
||||
|
||||
needle_first = *(unsigned char *)needle;
|
||||
|
||||
while (plen >= nlen && (p = static_cast<const uint8_t*>(memchr(p, needle_first, plen - nlen + 1))))
|
||||
{
|
||||
if (!memcmp(p, needle, nlen))
|
||||
return (void *)p;
|
||||
|
||||
p++;
|
||||
plen = hlen - (p - static_cast<const uint8_t*>(haystack));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Clearly, MS doesn't require enough headers for this */
|
||||
#include "winnls.h"
|
||||
#include "shobjidl.h"
|
||||
#include "objbase.h"
|
||||
#include "objidl.h"
|
||||
#include "shlguid.h"
|
||||
#include "strsafe.h"
|
||||
|
||||
HRESULT CreateShellLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszDesc)
|
||||
{
|
||||
std::wstring targetStr(lpszPathObj);
|
||||
for (wchar_t& ch : targetStr)
|
||||
if (ch == L'/')
|
||||
ch = L'\\';
|
||||
std::wstring linkStr(lpszPathLink);
|
||||
linkStr += L".lnk";
|
||||
for (wchar_t& ch : linkStr)
|
||||
if (ch == L'/')
|
||||
ch = L'\\';
|
||||
|
||||
HRESULT hres;
|
||||
IShellLink* psl;
|
||||
|
||||
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
|
||||
// has already been called.
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
IPersistFile* ppf;
|
||||
|
||||
// Set the path to the shortcut target and add the description.
|
||||
WCHAR targetBuf[MAX_PATH];
|
||||
WCHAR linkBuf[MAX_PATH];
|
||||
WCHAR* linkFinalPart = nullptr;
|
||||
GetFullPathNameW(linkStr.c_str(), MAX_PATH, linkBuf, &linkFinalPart);
|
||||
if (linkFinalPart != linkBuf)
|
||||
*(linkFinalPart-1) = L'\0';
|
||||
StringCbPrintfW(targetBuf, MAX_PATH, L"%s\\%s", linkBuf, targetStr.c_str());
|
||||
if (linkFinalPart != linkBuf)
|
||||
*(linkFinalPart - 1) = L'\\';
|
||||
psl->SetPath(targetBuf);
|
||||
psl->SetRelativePath(linkBuf, 0);
|
||||
psl->SetDescription(lpszDesc);
|
||||
|
||||
// Query IShellLink for the IPersistFile interface, used for saving the
|
||||
// shortcut in persistent storage.
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
// Save the link by calling IPersistFile::Save.
|
||||
hres = ppf->Save(linkBuf, TRUE);
|
||||
ppf->Release();
|
||||
}
|
||||
psl->Release();
|
||||
}
|
||||
return hres;
|
||||
}
|
||||
|
||||
HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize)
|
||||
{
|
||||
HRESULT hres;
|
||||
IShellLink* psl;
|
||||
WCHAR szGotPath[MAX_PATH];
|
||||
WCHAR szDescription[MAX_PATH];
|
||||
WIN32_FIND_DATA wfd;
|
||||
|
||||
*lpszPath = 0; // Assume failure
|
||||
|
||||
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
|
||||
// has already been called.
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
IPersistFile* ppf;
|
||||
|
||||
// Get a pointer to the IPersistFile interface.
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
// Load the shortcut.
|
||||
hres = ppf->Load(lpszLinkFile, STGM_READ);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
// Resolve the link.
|
||||
HWND hwnd = GetConsoleWindow();
|
||||
if (!hwnd)
|
||||
hwnd = GetTopWindow(nullptr);
|
||||
hres = psl->Resolve(hwnd, 0);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
// Get the path to the link target.
|
||||
hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
// Get the description of the target.
|
||||
hres = psl->GetDescription(szDescription, MAX_PATH);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release the pointer to the IPersistFile interface.
|
||||
ppf->Release();
|
||||
}
|
||||
|
||||
// Release the pointer to the IShellLink interface.
|
||||
psl->Release();
|
||||
}
|
||||
return hres;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue